Summary

Add back the functionality of Vec::from_elem by improving the vec![x; n] sugar to work with Clone x and runtime n.

Motivation

High demand, mostly. There are currently a few ways to achieve the behaviour of Vec::from_elem(elem, n):

// #1 let vec = Vec::new(); for i in range(0, n) { vec.push(elem.clone()) }
// #2 let vec = vec![elem; n]
// #3 let vec = Vec::new(); vec.resize(elem, n);
// #4 let vec: Vec<_> = (0..n).map(|_| elem.clone()).collect()
// #5 let vec: Vec<_> = iter::repeat(elem).take(n).collect();

None of these quite match the convenience, power, and performance of:

let vec = Vec::from_elem(elem, n)
  • #1 is verbose and slow, because each push requires a capacity check.
  • #2 only works for a Copy elem and const n.
  • #3 needs a temporary, but should be otherwise identical performance-wise.
  • #4 and #5 are considered verbose and noisy. They also need to clone one more time than other methods strictly need to.

However the issues for #2 are entirely artificial. It’s simply a side-effect of forwarding the impl to the identical array syntax. We can just make the code in the vec! macro better. This naturally extends the compile-timey [x; n] array sugar to the more runtimey semantics of Vec, without introducing “another way to do it”.

vec![100; 10] is also slightly less ambiguous than from_elem(100, 10), because the [T; n] syntax is part of the language that developers should be familiar with, while from_elem is just a function with arbitrary argument order.

vec![x; n] is also known to be 47% more sick-rad than from_elem, which was of course deprecated to due its lack of sick-radness.

Detailed design

Upgrade the current vec! macro to have the following definition:

macro_rules! vec { ($x:expr; $y:expr) => ( unsafe { use std::ptr; use std::clone::Clone; let elem = $x; let n: usize = $y; let mut v = Vec::with_capacity(n); let mut ptr = v.as_mut_ptr(); for i in range(1, n) { ptr::write(ptr, Clone::clone(&elem)); ptr = ptr.offset(1); v.set_len(i); } // No needless clones if n > 0 { ptr::write(ptr, elem); v.set_len(n); } v } ); ($($x:expr),*) => ( <[_] as std::slice::SliceExt>::into_vec( std::boxed::Box::new([$($x),*])) ); ($($x:expr,)*) => (vec![$($x),*]) }

(note: only the [x; n] branch is changed)

Which allows all of the following to work:

fn main() { println!("{:?}", vec![1; 10]); println!("{:?}", vec![Box::new(1); 10]); let n = 10; println!("{:?}", vec![1; n]); }

Drawbacks

Less discoverable than from_elem. All the problems that macros have relative to static methods.

Alternatives

Just un-delete from_elem as it was.

Unresolved questions

No.