Ownership
Ownership is one of the three central pillars in Rust's memory model, where the others are Borrowing and Lifetimes. Ownership can be summarized in the following three rules:
- Each value in Rust has a variable that’s called its owner.
- There can only be one owner at a time.
- When the owner goes out of scope, the value will be dropped.
When a value is transferred from one owner to another, this called a move. After the move, the previous owner can no longer access the value.
Example
fn foo(a: String, b: String) -> String {
// Here, the strings are owned by by the variables `a` and `b`.
// This moves `a` into `c`, so `c` becomes the new owner of the string.
// After this, `a` can no longer be used.
let c = a;
// Gives ownership of `b` to the `drop` function.
// After this, `b` can no longer be used.
drop(b);
// `clone()` makes a deep copy. `c` and `d` are now identical strings.
let d = c.clone();
// Returns `d`, which moves the value to the calling function
d
// Here `c` goes out of scope, while still owning a string.
// Therefore it is dropped and its destructors are run.
}
The Drop
trait
A scope is a region of code surrounded by curly braces ({}
). In the example above, the function foo
is a scope, and the parameters a
and b
as well as the local variables c
and d
only exist within this scope.
The ownership rules mention that values that go out of scope are dropped. Often, this means releasing a resource; for example, the String
type allocates memory and dropping it will free it. To do this, it implements the Drop
trait, which acts as a destructor. Whenever a value that implements Drop
goes out of scope, this destructor is run automatically.
The Copy
trait
Some values, such as primitive types, are very small, so it's cheap to copy them. These types usually implement the Copy
trait. This means that they are are copied instead of being moved like all other values. Example:
let a: i32 = 5;
let b = a;
let c = a;
let d = a + b - c;
// all four variables now own a number
This would not be possible if i32
didn't implement the Copy
trait, because then a
would be moved into b
and accessing a
after this would produce a compiler error. But thanks to the Copy
trait, this works as desired.
As Copy
types are constantly being duplicated and destroyed, Copy
is mutually exclusive with Drop
. Copyable types should only ever be used to simply store some data (such as an integer or a character) rather than control an external resource (such as memory or a file).