From b1cd35e6ab618f998f95c2d35b7fff2d9dbe65f2 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 11 Feb 2015 15:22:26 -0500 Subject: [PATCH 1/2] from_elem_with_love --- text/0000-from-elem-with-love.md | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 text/0000-from-elem-with-love.md diff --git a/text/0000-from-elem-with-love.md b/text/0000-from-elem-with-love.md new file mode 100644 index 00000000000..6e6897faf8d --- /dev/null +++ b/text/0000-from-elem-with-love.md @@ -0,0 +1,73 @@ +- Feature Name: from_elem_with_love +- Start Date: 2015-02-11 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add back `Vec::from_elem`. + +# 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` suffer from the untrusted iterator len problem performance wise (which is only a temporary +argument, this will be solved sooner rather than later), and are otherwise verbose and noisy. They also +need to clone one more time than other methods *strictly* need to. + +# Detailed design + +Just revert the code deletion. + +# Drawbacks + +Trivial API bloat, more ways to do the same thing. + +# Alternatives + +Make the `vec![elem; n]` form more powerful. There's literally no reason the author is aware of +for the restrictions it has. It would be shorter. It would be cooler. It would give less ways +to do the same thing. Also it better clarifies the subtle amiguity of what `Vec::from_elem(100, 10)` +produces. 100 10's? 10 100's? + +# Unresolved questions + +No. From bebaf82f19b6f3c5a3d90e4f377b3ec20419f057 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Thu, 12 Feb 2015 17:42:41 -0500 Subject: [PATCH 2/2] Swap alternative with primary design. Long live vec![x; n] --- text/0000-from-elem-with-love.md | 79 +++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 12 deletions(-) diff --git a/text/0000-from-elem-with-love.md b/text/0000-from-elem-with-love.md index 6e6897faf8d..2224ced9e4e 100644 --- a/text/0000-from-elem-with-love.md +++ b/text/0000-from-elem-with-love.md @@ -5,7 +5,7 @@ # Summary -Add back `Vec::from_elem`. +Add back the functionality of `Vec::from_elem` by improving the `vec![x; n]` sugar to work with Clone `x` and runtime `n`. # Motivation @@ -46,27 +46,82 @@ 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` +* `#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` suffer from the untrusted iterator len problem performance wise (which is only a temporary -argument, this will be solved sooner rather than later), and are otherwise verbose and noisy. They also -need to clone one more time than other methods *strictly* need to. +* `#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* artifical. 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 -Just revert the code deletion. +Upgrade the current `vec!` macro to have the following definition: + +```rust +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 -Trivial API bloat, more ways to do the same thing. +Less discoverable than from_elem. All the problems that macros have relative to static methods. # Alternatives -Make the `vec![elem; n]` form more powerful. There's literally no reason the author is aware of -for the restrictions it has. It would be shorter. It would be cooler. It would give less ways -to do the same thing. Also it better clarifies the subtle amiguity of what `Vec::from_elem(100, 10)` -produces. 100 10's? 10 100's? +Just un-delete from_elem as it was. # Unresolved questions