Add an unstable-book article about unsized_locals.

This commit is contained in:
Masaki Hara 2018-05-30 00:33:01 +09:00
parent 800f2c13a3
commit c72e87e30a

View 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;
}
};
}
```