Skip to content

Commit c72e87e

Browse files
committed
Add an unstable-book article about unsized_locals.
1 parent 800f2c1 commit c72e87e

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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+
11+
[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-coercions.md
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+
83+
However, the current implementation allows `MyTupleStruct(..)` to be unsized. This will be fixed in the future.
84+
85+
## By-value trait objects
86+
87+
With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
88+
89+
```rust
90+
#![feature(unsized_locals)]
91+
92+
trait Foo {
93+
fn foo(self) {}
94+
}
95+
96+
impl<T: ?Sized> Foo for T {}
97+
98+
fn main() {
99+
let slice: Box<[i32]> = Box::new([1, 2, 3]);
100+
<[i32] as Foo>::foo(*slice);
101+
}
102+
```
103+
104+
And `Foo` will also be object-safe. However, this object-safety is not yet implemented.
105+
106+
```rust,ignore
107+
#![feature(unsized_locals)]
108+
109+
trait Foo {
110+
fn foo(self) {}
111+
}
112+
113+
impl<T: ?Sized> Foo for T {}
114+
115+
fn main () {
116+
let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
117+
// doesn't compile yet
118+
<dyn Foo as Foo>::foo(*slice);
119+
}
120+
```
121+
122+
Unfortunately, this is not implemented yet.
123+
124+
One of the objectives of this feature is to allow `Box<dyn FnOnce>`, instead of `Box<dyn FnBox>` in the future. See [#28796] for details.
125+
126+
[#28796]: https://github.com/rust-lang/rust/issues/28796
127+
128+
## Variable length arrays
129+
130+
The RFC also describes an extension to the array literal syntax `[e; n]`: you'll be able to specify non-const `n` to allocate variable length arrays on the stack.
131+
132+
```rust,ignore
133+
#![feature(unsized_locals)]
134+
135+
fn mergesort<T: Ord>(a: &mut [T]) {
136+
let mut tmp = [T; a.len()];
137+
// ...
138+
}
139+
140+
fn main() {
141+
let mut a = [3, 1, 5, 6];
142+
mergesort(&mut a);
143+
assert_eq!(a, [1, 3, 5, 6]);
144+
}
145+
```
146+
147+
VLAs are not implemented yet.
148+
149+
## Advisory on stack usage
150+
151+
It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
152+
153+
- When you need a by-value trait objects.
154+
- When you really need a fast allocation of small temporary arrays.
155+
156+
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
157+
158+
```rust
159+
#![feature(unsized_locals)]
160+
161+
fn main() {
162+
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
163+
let _x = {{{{{{{{{{*x}}}}}}}}}};
164+
}
165+
```
166+
167+
and the code
168+
169+
```rust
170+
#![feature(unsized_locals)]
171+
172+
fn main() {
173+
for _ in 0..10 {
174+
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
175+
let _x = *x;
176+
}
177+
}
178+
```
179+
180+
will unnecessarily extend the stack frame.
181+
182+
Allocation will be improved in the future, but there are still examples that are difficult to optimize:
183+
184+
```rust
185+
#![feature(unsized_locals)]
186+
187+
fn main() {
188+
let mut counter = 10;
189+
let x = loop {
190+
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
191+
let x = *x;
192+
if counter > 0 {
193+
counter -= 1;
194+
} else {
195+
break x;
196+
}
197+
};
198+
}
199+
```

0 commit comments

Comments
 (0)