about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMasaki Hara <ackie.h.gmai@gmail.com>2018-05-30 00:33:01 +0900
committerMasaki Hara <ackie.h.gmai@gmail.com>2018-08-19 08:07:33 +0900
commitc72e87e30a46a1d0a97954d5fed965c88a0b7ce2 (patch)
treedd4259a69afa4817d426b1819a9cdee827ba547b
parent800f2c13a3f4213648f301dcd4e10d80b1e6ea38 (diff)
downloadrust-c72e87e30a46a1d0a97954d5fed965c88a0b7ce2.tar.gz
rust-c72e87e30a46a1d0a97954d5fed965c88a0b7ce2.zip
Add an unstable-book article about unsized_locals.
-rw-r--r--src/doc/unstable-book/src/language-features/unsized-locals.md199
1 files changed, 199 insertions, 0 deletions
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<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;
+        }
+    };
+}
+```