- Feature Name: direct to stable, because it modifies a stable macro
- Start Date: 2015-02-11
- RFC PR: rust-lang/rfcs#832
- Rust Issue: rust-lang/rust#22414
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 eachpush
requires a capacity check.#2
only works for a Copyelem
and constn
.#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.