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