Add an unstable-book article about unsized_locals.
This commit is contained in:
parent
800f2c13a3
commit
c72e87e30a
199
src/doc/unstable-book/src/language-features/unsized-locals.md
Normal file
199
src/doc/unstable-book/src/language-features/unsized-locals.md
Normal file
@ -0,0 +1,199 @@
|
||||
# `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-coercions.md
|
||||
|
||||
```rust
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
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,ignore
|
||||
#![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
|
||||
}
|
||||
```
|
||||
|
||||
However, the current implementation allows `MyTupleStruct(..)` to be unsized. This will be fixed in the future.
|
||||
|
||||
## By-value trait objects
|
||||
|
||||
With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
|
||||
|
||||
```rust
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
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. However, this object-safety is not yet implemented.
|
||||
|
||||
```rust,ignore
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
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);
|
||||
}
|
||||
```
|
||||
|
||||
Unfortunately, this is not implemented yet.
|
||||
|
||||
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.
|
||||
|
||||
[#28796]: https://github.com/rust-lang/rust/issues/28796
|
||||
|
||||
## Variable length arrays
|
||||
|
||||
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.
|
||||
|
||||
```rust,ignore
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn mergesort<T: Ord>(a: &mut [T]) {
|
||||
let mut tmp = [T; 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.
|
||||
|
||||
## 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.
|
||||
|
||||
Allocation will be improved in the future, but there are still examples that are difficult to optimize:
|
||||
|
||||
```rust
|
||||
#![feature(unsized_locals)]
|
||||
|
||||
fn main() {
|
||||
let mut counter = 10;
|
||||
let x = loop {
|
||||
let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
|
||||
let x = *x;
|
||||
if counter > 0 {
|
||||
counter -= 1;
|
||||
} else {
|
||||
break x;
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue
Block a user