Skip to content

Commit f8a83dd

Browse files
authored
Merge pull request #2203 from Centril/rfc/const-repeat-expr
RFC: Constants in array repeat expressions
2 parents 3af4a21 + 3c6efc2 commit f8a83dd

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed

text/2203-const-repeat-expr.md

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
- Feature Name: `const_repeat_expr`
2+
- Start Date: 2017-10-20
3+
- RFC PR: [rust-lang/rfcs#2203](https://github.com/rust-lang/rfcs/pull/2203)
4+
- Rust Issue: [rust-lang/rust#49147](https://github.com/rust-lang/rust/issues/49147)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Relaxes the rules for repeat expressions, `[x; N]` such that `x` may also be
10+
`const` *(strictly speaking rvalue promotable)*, in addition to `typeof(x): Copy`.
11+
The result of `[x; N]` where `x` is `const` is itself also `const`.
12+
13+
# Motivation
14+
[motivation]: #motivation
15+
16+
[RFC 2000, `const_generics`]: https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md
17+
[`const_default` RFC]: https://github.com/Centril/rfcs/blob/rfc/const-default/text/0000-const-default.md
18+
19+
[RFC 2000, `const_generics`] introduced the ability to have generically sized
20+
arrays. Even with that RFC, it is currently impossible to create such an array
21+
that is also `const`. Creating an array that is `const` may for example be
22+
useful for the [`const_default` RFC] which proposes the following trait:
23+
24+
```rust
25+
pub trait ConstDefault { const DEFAULT: Self; }
26+
```
27+
28+
To add an implementation of this trait for an array of any size where the
29+
elements of type `T` are `ConstDefault`, as in:
30+
31+
```rust
32+
impl<T: ConstDefault, const N: usize> ConstDefault for [T; N] {
33+
const DEFAULT: Self = [T::DEFAULT; N];
34+
}
35+
```
36+
37+
[`mem::uninitialized()`]: https://doc.rust-lang.org/nightly/std/mem/fn.uninitialized.html
38+
39+
In the example given by [`mem::uninitialized()`], a value of type
40+
`[Vec<u32>; 1000]` is created and filled. With this RFC, and when `Vec::new()`
41+
becomes const, the user can simply write:
42+
43+
```rust
44+
let data = [Vec::<u32>::new(); 1000];
45+
println!("{:?}", &data[0]);
46+
```
47+
48+
this removes one common reason to use `uninitialized()` which **"is incredibly
49+
dangerous"**.
50+
51+
# Guide-level explanation
52+
[guide-level-explanation]: #guide-level-explanation
53+
54+
You have a variable or expression `X` which is const, for example:
55+
56+
```rust
57+
type T = Option<Box<u32>>;
58+
const X: T = None;
59+
```
60+
61+
Now, you'd like to use array repeat expressions `[X; N]` to create an array
62+
containing a bunch of `X`es. Sorry, you are out of luck!
63+
64+
But with this RFC, you can now write:
65+
66+
```rust
67+
const X: T = None;
68+
const arr: [T; 100] = [X; 100];
69+
```
70+
71+
or, if you wish to modify the array later:
72+
73+
```rust
74+
const X: T = None;
75+
let mut arr = [X; 100];
76+
arr[0] = Some(Box::new(1));
77+
```
78+
79+
# Reference-level explanation
80+
[reference-level-explanation]: #reference-level-explanation
81+
82+
Values which are `const` are freely duplicatable as seen in the following
83+
example which compiles today. This is also the case with `Copy`. Therefore, the
84+
value `X` in the repeat expression may be simply treated as if it were of a
85+
`Copy` type.
86+
87+
```rust
88+
fn main() {
89+
type T = Option<Box<u32>>;
90+
const X: T = None;
91+
let mut arr = [X, X];
92+
arr[0] = Some(Box::new(1));
93+
}
94+
```
95+
96+
Thus, the compiler may rewrite the following:
97+
98+
```rust
99+
fn main() {
100+
type T = Option<Box<u32>>;
101+
const X: T = None;
102+
let mut arr = [X; 2];
103+
arr[0] = Some(Box::new(1));
104+
}
105+
```
106+
107+
internally as:
108+
109+
```rust
110+
fn main() {
111+
type T = Option<Box<u32>>;
112+
113+
// This is the value to be repeated.
114+
// In this case, a panic won't happen, but if it did, that panic
115+
// would happen during compile time at this point and not later.
116+
const X: T = None;
117+
118+
let mut arr = {
119+
let mut data: [T; 2];
120+
121+
unsafe {
122+
data = mem::uninitialized();
123+
124+
let mut iter = (&mut data[..]).into_iter();
125+
while let Some(elem) = iter.next() {
126+
// ptr::write does not run destructor of elem already in array.
127+
// Since X is const, it can not panic at this point.
128+
ptr::write(elem, X);
129+
}
130+
}
131+
132+
data
133+
};
134+
135+
arr[0] = Some(Box::new(1));
136+
}
137+
```
138+
139+
Additionally, the pass that checks `const`ness must treat `[expr; N]` as a
140+
`const` value such that `[expr; N]` is assignable to a `const` item as well
141+
as permitted inside a `const fn`.
142+
143+
Strictly speaking, the set of values permitted in the expression `[expr; N]`
144+
are those where `is_rvalue_promotable(expr)` or `typeof(expr): Copy`.
145+
Specifically, in `[expr; N]` the expression `expr` is evaluated:
146+
+ never, if `N == 0`,
147+
+ one time, if `N == 1`,
148+
+ `N` times, otherwise.
149+
150+
For values that are not freely duplicatable, evaluating `expr` will result in
151+
a move, which results in an error if `expr` is moved more than once (including
152+
moves outside of the repeat expression). These semantics are intentionally
153+
conservative and intended to be forward-compatible with a more expansive
154+
`is_const(expr)` check.
155+
156+
# Drawbacks
157+
[drawbacks]: #drawbacks
158+
159+
It might make the semantics of array initializers more fuzzy. The RFC, however,
160+
argues that the change is quite intuitive.
161+
162+
# Rationale and alternatives
163+
[alternatives]: #alternatives
164+
165+
[`ptr::write(..)`]: https://doc.rust-lang.org/nightly/std/ptr/fn.write.html
166+
167+
The alternative, in addition to simply not doing this, is to modify a host of
168+
other constructs such as [`mem::uninitialized()`], for loops over iterators,
169+
[`ptr::write`] to be `const`, which is is a larger change. The design offered by
170+
this RFC is therefore the simplest and most non-intrusive design. It is also
171+
the most consistent.
172+
173+
Another alternative is to allow a more expansive set of values `is_const(expr)`
174+
rather than `is_rvalue_promotable(expr)`. A consequence of this is that checking
175+
constness would be done earlier on the HIR. Instead, checking if `expr` is
176+
rvalue promotable can be done on the MIR and does not require significant
177+
changes to the compiler. If we decide to expand to `is_const(expr)` in the
178+
future, we may still do so as the changes proposed in this RFC are
179+
compatible with such future changes.
180+
181+
The impact of not doing this change is to not enable generically sized arrays to
182+
be `const` as well as encouraging the use of `mem::uninitialized`.
183+
184+
# Unresolved questions
185+
[unresolved]: #unresolved-questions
186+
187+
There are no unresolved questions.

0 commit comments

Comments
 (0)