rust/src/doc/trpl/move-semantics.md
2015-04-19 00:54:42 +05:30

2.8 KiB
Raw Blame History

% Move Semantics

An important aspect of ownership is move semantics. Move semantics control how and when ownership is transferred between bindings.

For example, consider a type like Vec<T>, which owns its contents:

let v = vec![1, 2, 3];

I can assign this vector to another binding:

let v = vec![1, 2, 3];

let v2 = v;

But, if we try to use v afterwards, we get an error:

let v = vec![1, 2, 3];

let v2 = v;

println!("v[0] is: {}", v[0]);

It looks like this:

error: use of moved value: `v`
println!("v[0] is: {}", v[0]);
                        ^

A similar thing happens if we define a function which takes ownership, and try to use something after weve passed it as an argument:

fn take(v: Vec<i32>) {
    // what happens here isnt important.
}

let v = vec![1, 2, 3];

take(v);

println!("v[0] is: {}", v[0]);

Same error: “use of moved value.” When we transfer ownership to something else, we say that weve moved the thing we refer to. You dont need some sort of special annotation here, its the default thing that Rust does.

The details

The reason that we cannot use a binding after weve moved it is subtle, but important. When we write code like this:

let v = vec![1, 2, 3];

let v2 = v;

The first line creates some data for the vector on the stack, v. The vectors data, however, is stored on the heap, and so it contains a pointer to that data. When we move v to v2, it creates a copy of that data, for v2. Which would mean two pointers to the contents of the vector on the heap. That would be a problem: it would violate Rusts safety guarantees by introducing a data race. Therefore, Rust forbids using v after weve done the move.

Its also important to note that optimizations may remove the actual copy of the bytes, depending on circumstances. So it may not be as inefficient as it initially seems.

Copy types

Weve established that when ownership is transferred to another binding, you cannot use the original binding. However, theres a trait that changes this behavior, and its called Copy. We havent discussed traits yet, but for now, you can think of them as an annotation to a particular type that adds extra behavior. For example:

let v = 1;

let v2 = v;

println!("v is: {}", v);

In this case, v is an i32, which implements the Copy trait. This means that, just like a move, when we assign v to v2, a copy of the data is made. But, unlike a move, we can still use v afterward. This is because an i32 has no pointers to data somewhere else, copying it is a full copy.

We will discuss how to make your own types Copy in the traits section.