diff --git a/text/0000-own.md b/text/0000-own.md new file mode 100644 index 00000000000..02f7250a100 --- /dev/null +++ b/text/0000-own.md @@ -0,0 +1,385 @@ +- Feature Name: own +- Start Date: Tue Mar 10 23:39:14 CET 2015 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Add reference and slice types that take ownership of the objects they reference. + +# Examples + +```rust +fn f() { + let x: String = String::new(); + let x_ref: &own String = &own x; + let y: String = *x_ref; +} + +fn g() { + let xs: [String; 2] = [String::new(), String::new()]; + let x_refs: &own [String] = &own xs[..]; + let ys: Vec = x_refs.into_iter().collect(); +} +``` + +# Motivation + +Today it is not easily possible to express the idea of a reference that you can +move out of. It can be emulated with enums: + +```rust +fn f() { + let x: Option = Some(String::new()); + let x_ref: &mut Option = &mut x; + let y: String = x_ref.take().unwrap(); +} +``` + +There are some disadvantages related to performance and ergonomics: + +- Having to use `Some(variable)` to create a movable reference is inconvenient. +- `&mut Option` is uglier than `&own T`. +- In the code above, the destructors of both `x` and `y` will have to run even + though the `String` has been moved out of `x`. +- `&mut Option` is not safe in the way references are safe in Rust. A `&T` + always points to a valid `T`. A `&mut Option` can point to `None`. The + receiver either has to deal with panics or check (and handle) the presence of + `None`. (Similar to languages such as C# where you can get + `NullReferenceExceptions`.) + +Another problem are traits. The following is currently not possible: + +```rust +trait GivesYouAString { + fn string(self) -> String; +} + +impl GivesYouAString for String { + fn string(self) -> String { self } +} + +fn f() { + let x = String::new(); + let y = &mut x as &mut GivesYouAString; // not object safe + let z = y.string(); // cannot move out of mutable reference +} +``` + +With `&own` this would be easy: + +```rust +trait GivesYouAString { + fn string(&own self) -> String; +} + +impl GivesYouAString for String { + fn string(&own self) -> String { *self } +} + +fn f() { + let x = String::new(); + let y = &own x as &own GivesYouAString; + let z = y.string(); +} +``` + +Right now you can work around this by using: + +```rust +impl GivesYouAString for String { + fn string(self: Box) -> String { *self } +} + +// ... + + let y = Box::new(x) as Box; + let z = y.string(); +} +``` + +but this requires an additional allocation. + +A practical use case of this is the `Any` trait. With move semantics we can pass +an arbitrary type to a function and consume the contained object: + +```rust +fn append_hello_world(x: &own Any) -> String { + if x.is::() { + x.downcast_move::().unwrap().push("hello world") + } else if x.is::() { + x.downcast_move::().unwrap().to_string().push("hello world") + } else { + String::new("hello world") + } +} +``` + +---- + +For slices the situation is somewhat worse. To move out of a slice, the user has +to replace every element in the slice by `Option`. If the size of the slice +is unknown at compile time, then this requires a `Vec>` allocation. +Combining all of these aspects, one might as well just pass a `Vec` directly. + +With `&own` slices and pointers it's quite easy to implement variadic functions +that take arbitrary, type-safe, arguments. + +```rust +/// Collects all of the arguments that are strings into a vector. +fn collect_strings(args: &own [&own Any]) -> Vec { + let mut strings = vec!(); + for arg in args.into_iter() { + if arg.is::() { + let string = x.downcast_move::().unwrap(); + strings.push(string); + } + } + strings +} +``` + +which can be used like this: + +```rust +fn f() { + let arg1 = String::new(); + let arg2 = 1u32; + let arg3 = String::new(); + let strings = collect_strings(&own [&own arg1, &own arg2, &own arg3][..]); +} +``` + +--- + +Even without `&own` slices it is possible to implement variadic functions with +a little bit of compiler magic: + +```rust +fn f(args: ..&own Any) { + // args is an anonymous type that implements Iterator +} + +fn g() { + let arg1 = String::new(); + let arg2 = 1u32; + let arg3 = String::new(); + f(&own arg1, &own arg2, &own arg3); +} +``` + +If `Any` were a built-in type, then this could even be simplified to + +```rust +fn f(args: ..) { + // args is an anonymous type that implements Iterator +} + +fn g() { + let arg1 = String::new(); + let arg2 = 1u32; + let arg3 = String::new(); + f(arg1, arg2, arg3); +} +``` + +But this is somewhat off-topic. + +# Detailed design + +A `&own T` reference looks exactly like a `&mut T` reference, i.e., if `T` is a +sized type, then it's simply a pointer to the referenced data. If `T` is a +trait or slice (`[U]`), then it's a fat pointer. + +A `&own T` reference behaves exactly like a `&mut T` reference except that you +can move out of it via dereferencing. This consumes the reference. If the +reference is not explicitly consumed, the compiler will consume the reference +implicitly. For example: + +```rust +fn f(_x: &own String) { +} +``` + +Modulo optimization, this is equivalent to + +```rust +fn f(_x: &own String) { + let _unused = *_x; +} +``` + +Creating a `&own T` reference makes the referenced object indefinitely +inaccessible except through the `&own` reference. + +```rust +fn f(x: String) { + { + let _y = &own x; + } + x.push(""); // error: x has been moved +} +``` + +Creating a `&own T` reference moves the drop obligation for the referenced +object into the reference. For example: + +```rust +fn f(x: String, flag: bool) { + if flag { + g(&own x); + } else { + h(); + } +} +``` + +Assuming non-zeroing drop, the compiler will generate the following pseudo code: + +```rust +fn f(x: String, flag: bool) { + let mut x_needs_drop = true; + + if flag { + x_needs_drop = false; + g(&own x); + } else { + h(); + } + + if x_needs_drop { + drop x; + // This is pseudo code because the user cannot access `x` at this point. + } +} +``` + +Creating a `&own T` reference, however, does not move the drop obligation for +the container into the reference. For example: + +```rust +fn f(x: Box, flag: bool) { + if flag { + g(&own *x); + } else { + h(); + } +} +``` + +Again assuming non-zeroing drops, the compiler generates + +```rust +fn f(x: Box, flag: bool) { + let mut x_needs_drop = true; + + if flag { + x_needs_drop = false; + g(&own *x); + } else { + h(); + } + + if x_needs_drop { + drop *x; + } + deallocate(x); +} +``` + +This means that there are two drop flags: One for the container and one for the +contained object. (With zeroing drops both of these flags are implicit.) + +It's possible to take a sub-`&own` reference if the type does not implement +`Drop`. For example: + +```rust +struct X { + a: String, + b: String, +} + +fn f(x: &own X) -> &own String { + &own x.b +} + +// This is equivalent to + +fn g(x: &own X) -> &own String { + let X { a: _, b: ref out b } = *x; + b +} + +// One could even write + +fn h(x: &own X) -> (&own String, &own String) { + let a = &own x.a; + let b = &own x.b; + (a, b) +} +``` + +A `&own T` supports implicit coercions to `&mut T` and `&T`: + +```rust +fn f(x: String) { + g(&own x); + // implicit move out of the anonymous reference here +} + +fn g(x: &mut T) { +} +``` + +## Slices + +Even though slices are not special, we'll describe the behavior that can be +expected from `&own [T]` slices here: + +Subslicing causes the objects that are no longer in the slice to be dropped. + +Taking a `&own` reference to a single element causes all other elements to be +dropped: + +```rust +fn f(xs: &own [String]) { + let x = &own xs[0]; +} +``` + +At the moment it is not possible to move out of custom containers such as +`VecDeque`. However, this is not a serious problem because we can still +create owning slices via a method: + +```rust +impl VecDeque { + pub fn owned_sices(&mut self) -> (&own [T], &own [T]) { + let slices = mem::transmute(self.as_slices()); + self.tail = 0; + self.head = 0; + slices + } +} +``` + +The two slices that have been returned own their content and once the slices +have been dropped the empty `VecDeque` is accessible again. + +# Drawbacks + +You can't create `&own` slices with the `&own vec[..]` syntax because Rust +currently does not allow moving out of containers other that `Box`. + +# Alternatives + +## What other designs have been considered? + +None + +## What is the impact of not doing this? + +Can't move out of references. + +# Unresolved questions + +None right now.