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