Skip to content

Commit feba58e

Browse files
committed
Merge branch 'Gankro-from_elem' into HEAD
2 parents dfd33cc + ada79e6 commit feba58e

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

text/0000-from-elem-with-love.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
- Feature Name: direct to stable, because it modifies a stable macro
2+
- Start Date: 2015-02-11
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/832
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/22414
5+
6+
# Summary
7+
8+
Add back the functionality of `Vec::from_elem` by improving the `vec![x; n]` sugar to work with Clone `x` and runtime `n`.
9+
10+
# Motivation
11+
12+
High demand, mostly. There are currently a few ways to achieve the behaviour of `Vec::from_elem(elem, n)`:
13+
14+
```
15+
// #1
16+
let vec = Vec::new();
17+
for i in range(0, n) {
18+
vec.push(elem.clone())
19+
}
20+
```
21+
22+
```
23+
// #2
24+
let vec = vec![elem; n]
25+
```
26+
27+
```
28+
// #3
29+
let vec = Vec::new();
30+
vec.resize(elem, n);
31+
```
32+
33+
```
34+
// #4
35+
let vec: Vec<_> = (0..n).map(|_| elem.clone()).collect()
36+
```
37+
38+
```
39+
// #5
40+
let vec: Vec<_> = iter::repeat(elem).take(n).collect();
41+
```
42+
43+
None of these quite match the convenience, power, and performance of:
44+
45+
```
46+
let vec = Vec::from_elem(elem, n)
47+
```
48+
49+
* `#1` is verbose *and* slow, because each `push` requires a capacity check.
50+
* `#2` only works for a Copy `elem` and const `n`.
51+
* `#3` needs a temporary, but should be otherwise identical performance-wise.
52+
* `#4` and `#5` are considered verbose and noisy. They also need to clone one more
53+
time than other methods *strictly* need to.
54+
55+
However the issues for `#2` are *entirely* artifical. It's simply a side-effect of
56+
forwarding the impl to the identical array syntax. We can just make the code in the
57+
`vec!` macro better. This naturally extends the compile-timey `[x; n]` array sugar
58+
to the more runtimey semantics of Vec, without introducing "another way to do it".
59+
60+
`vec![100; 10]` is also *slightly* less ambiguous than `from_elem(100, 10)`,
61+
because the `[T; n]` syntax is part of the language that developers should be
62+
familiar with, while `from_elem` is just a function with arbitrary argument order.
63+
64+
`vec![x; n]` is also known to be 47% more sick-rad than `from_elem`, which was
65+
of course deprecated to due its lack of sick-radness.
66+
67+
# Detailed design
68+
69+
Upgrade the current `vec!` macro to have the following definition:
70+
71+
```rust
72+
macro_rules! vec {
73+
($x:expr; $y:expr) => (
74+
unsafe {
75+
use std::ptr;
76+
use std::clone::Clone;
77+
78+
let elem = $x;
79+
let n: usize = $y;
80+
let mut v = Vec::with_capacity(n);
81+
let mut ptr = v.as_mut_ptr();
82+
for i in range(1, n) {
83+
ptr::write(ptr, Clone::clone(&elem));
84+
ptr = ptr.offset(1);
85+
v.set_len(i);
86+
}
87+
88+
// No needless clones
89+
if n > 0 {
90+
ptr::write(ptr, elem);
91+
v.set_len(n);
92+
}
93+
94+
v
95+
}
96+
);
97+
($($x:expr),*) => (
98+
<[_] as std::slice::SliceExt>::into_vec(
99+
std::boxed::Box::new([$($x),*]))
100+
);
101+
($($x:expr,)*) => (vec![$($x),*])
102+
}
103+
```
104+
105+
(note: only the `[x; n]` branch is changed)
106+
107+
Which allows all of the following to work:
108+
109+
```
110+
fn main() {
111+
println!("{:?}", vec![1; 10]);
112+
println!("{:?}", vec![Box::new(1); 10]);
113+
let n = 10;
114+
println!("{:?}", vec![1; n]);
115+
}
116+
```
117+
118+
# Drawbacks
119+
120+
Less discoverable than from_elem. All the problems that macros have relative to static methods.
121+
122+
# Alternatives
123+
124+
Just un-delete from_elem as it was.
125+
126+
# Unresolved questions
127+
128+
No.

0 commit comments

Comments
 (0)