Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add &own T #965

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
385 changes: 385 additions & 0 deletions text/0000-own.md
Original file line number Diff line number Diff line change
@@ -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<String> = 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<String> = Some(String::new());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let mut x

let x_ref: &mut Option<String> = &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<T>` 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<T>` is not safe in the way references are safe in Rust. A `&T`
always points to a valid `T`. A `&mut Option<T>` 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<Self>) -> String { *self }
}

// ...

let y = Box::new(x) as Box<GivesYouAString>;
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::<String>() {
x.downcast_move::<String>().unwrap().push("hello world")
} else if x.is::<i32>() {
x.downcast_move::<i32>().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<T>`. If the size of the slice
is unknown at compile time, then this requires a `Vec<Option<T>>` allocation.
Combining all of these aspects, one might as well just pass a `Vec<T>` 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<String> {
let mut strings = vec!();
for arg in args.into_iter() {
if arg.is::<String>() {
let string = x.downcast_move::<String>().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<Item=&own Any>
}

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<Item=&own Any>
}

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<String>, flag: bool) {
if flag {
g(&own *x);
} else {
h();
}
}
```

Again assuming non-zeroing drops, the compiler generates

```rust
fn f(x: Box<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;
}
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<T>`. However, this is not a serious problem because we can still
create owning slices via a method:

```rust
impl<T> VecDeque<T> {
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.