1.9.1. The Core Idea of Ownership

The core idea of Rust’s memory model is that every value has exactly one owner. In other words, only one place — usually a scope — is responsible for freeing each value.

This behavior is enforced by the borrow checker. If a value is moved — for example by assigning it to a new variable, pushing it into a Vec, placing it on the heap, and so on — then the owner becomes the new location.

The owner is really just a location in memory; the place where the data lives is the value’s owner. A move means the data is transferred from one location to another, and the new location becomes the owner.

However, some types do not follow this rule: if a value’s type implements the Copy trait, then reassignment performs a copy rather than a move. That is, a copy of the value is placed in the new location.

1.9.2. How to Implement the Copy Trait

Types that implement Copy must be able to duplicate their values bit by bit.

Types that cannot implement Copy naturally include:

  • Types that contain non-Copy types
  • Types that must perform special resource-release work when their values are dropped

Why?
Imagine Box<T> implemented Copy. If you assigned box1 = box2, then both variables would believe they owned a heap allocation that belonged exclusively to them. When they went out of scope, both would try to free that memory, causing a double free.

1.9.3. Dropping Values

When a value is no longer needed, its owner deletes it.

Dropping — or discarding — a value happens when it goes out of scope. Types recursively drop the values they contain. For example, deleting a complex type can require deleting many values.

Rust does not drop the same value more than once because of ownership. If a variable contains references to other values that it does not own, then deleting that variable does not delete the other values.

That may be hard to understand, so let’s look at a simple example:

fn main() {  
    let x1 = 42;  
    let y1 = Box::new(x1);  

    {  
        let z = (x1, y1);  
    }  

    let x2 = x1;  
}

Enter fullscreen mode Exit fullscreen mode

  • x1 is an i32, and y1 is a Box<i32> that owns a heap allocation containing a copy of x1’s value (because i32 is Copy, Box::new(x1) does not borrow x1)
  • {} creates a new scope, and z is created inside that smaller scope
  • z is a tuple whose value is (x1, y1). x1 is an i32 and implements Copy, so x1 copies its value into z; y1 is a Box<i32> and does not implement Copy, so it cannot be copied and instead transfers ownership to z
  • After leaving the inner scope, x1 is used again. x1 is still valid because it copied its value into z and remains usable itself
  • y1 becomes invalid after being assigned to z because ownership moved to z

1.9.4. The Order of Dropping Values

  • Variables, including function parameters, are dropped in reverse order of declaration.
  • Nested values are dropped in source order. In the example above, when the inner scope ends, z is dropped: it first drops its first element (the copied i32), then its second element (the Box). Because y1 was moved into z, y1 itself is not dropped again afterward.

Note: Rust does not currently allow self-referential values inside a single value.