1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
# `unsized_locals`
The tracking issue for this feature is: [#48055]
[#48055]: https://github.com/rust-lang/rust/issues/48055
------------------------
This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md
```rust
#![allow(incomplete_features)]
#![feature(unsized_locals, unsized_fn_params)]
use std::any::Any;
fn main() {
let x: Box<dyn Any> = Box::new(42);
let x: dyn Any = *x;
// ^ unsized local variable
// ^^ unsized temporary
foo(x);
}
fn foo(_: dyn Any) {}
// ^^^^^^ unsized argument
```
The RFC still forbids the following unsized expressions:
```rust,compile_fail
#![feature(unsized_locals)]
use std::any::Any;
struct MyStruct<T: ?Sized> {
content: T,
}
struct MyTupleStruct<T: ?Sized>(T);
fn answer() -> Box<dyn Any> {
Box::new(42)
}
fn main() {
// You CANNOT have unsized statics.
static X: dyn Any = *answer(); // ERROR
const Y: dyn Any = *answer(); // ERROR
// You CANNOT have struct initialized unsized.
MyStruct { content: *answer() }; // ERROR
MyTupleStruct(*answer()); // ERROR
(42, *answer()); // ERROR
// You CANNOT have unsized return types.
fn my_function() -> dyn Any { *answer() } // ERROR
// You CAN have unsized local variables...
let mut x: dyn Any = *answer(); // OK
// ...but you CANNOT reassign to them.
x = *answer(); // ERROR
// You CANNOT even initialize them separately.
let y: dyn Any; // OK
y = *answer(); // ERROR
// Not mentioned in the RFC, but by-move captured variables are also Sized.
let x: dyn Any = *answer();
(move || { // ERROR
let y = x;
})();
// You CAN create a closure with unsized arguments,
// but you CANNOT call it.
// This is an implementation detail and may be changed in the future.
let f = |x: dyn Any| {};
f(*answer()); // ERROR
}
```
## By-value trait objects
With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
```rust
#![feature(unsized_fn_params)]
trait Foo {
fn foo(self) {}
}
impl<T: ?Sized> Foo for T {}
fn main() {
let slice: Box<[i32]> = Box::new([1, 2, 3]);
<[i32] as Foo>::foo(*slice);
}
```
And `Foo` will also be object-safe.
```rust
#![feature(unsized_fn_params)]
trait Foo {
fn foo(self) {}
}
impl<T: ?Sized> Foo for T {}
fn main () {
let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
// doesn't compile yet
<dyn Foo as Foo>::foo(*slice);
}
```
One of the objectives of this feature is to allow `Box<dyn FnOnce>`.
## Variable length arrays
The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
```rust,ignore (not-yet-implemented)
#![feature(unsized_locals)]
fn mergesort<T: Ord>(a: &mut [T]) {
let mut tmp = [T; dyn a.len()];
// ...
}
fn main() {
let mut a = [3, 1, 5, 6];
mergesort(&mut a);
assert_eq!(a, [1, 3, 5, 6]);
}
```
VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
## Advisory on stack usage
It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
- When you need a by-value trait objects.
- When you really need a fast allocation of small temporary arrays.
Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
```rust
#![feature(unsized_locals)]
fn main() {
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
let _x = {{{{{{{{{{*x}}}}}}}}}};
}
```
and the code
```rust
#![feature(unsized_locals)]
fn main() {
for _ in 0..10 {
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
let _x = *x;
}
}
```
will unnecessarily extend the stack frame.
|