From c72e87e30a46a1d0a97954d5fed965c88a0b7ce2 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Wed, 30 May 2018 00:33:01 +0900 Subject: [PATCH] Add an unstable-book article about unsized_locals. --- .../src/language-features/unsized-locals.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/unsized-locals.md diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md new file mode 100644 index 00000000000..ff5a6bcfbf7 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/unsized-locals.md @@ -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 = 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 { + content: T, +} + +struct MyTupleStruct(T); + +fn answer() -> Box { + 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 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 Foo for T {} + +fn main () { + let slice: Box = Box::new([1, 2, 3]); + // doesn't compile yet + ::foo(*slice); +} +``` + +Unfortunately, this is not implemented yet. + +One of the objectives of this feature is to allow `Box`, instead of `Box` 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(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; + } + }; +} +```