What is ownership?

A key concept in Rust's memory management

Last updated on March 18, 2023. Created on March 17, 2023.

Rust does not manage it’s memory using Garbage collection or trough explicit (de-)allocation but uses a concept called “ownership” based on explicit rules.

In this post we will focus on values with an unknown size at compile time in other words values that are stored on the “Heap”.

1. Each value has an owner

// The variable 'owner' owns the String "Book"
let owner = String::from("Book");

Memory layout

A string’s data is dynamic so it lives on the heap, the pointer, the length and capacity (the preallocated/reserved space before new memory needs to be allocated) lives on the stack.

2. There can only be one owner at a time

let owner = String::from("Book");

// The variable new_owner now becomes the owner of the String,
// in rust this is called a move.
let new_owner = owner;

println!("{0}", owner); // 💥 Compilation Error 💥

The compiler’s output is just amazing ❤️

cargo run
   Compiling package v0.1.0 (/path)
error[E0382]: borrow of moved value: `owner`
  --> src/main.rs:10:21
   |
4  |     let owner = String::from("Book");
   |         ----- move occurs because `owner` has type `String`,
                   which does not implement the `Copy` trait
...
8  |     let new_owner = owner;
   |                     ----- value moved here
9  |
10 |     println!("{0}", owner); // 💥 Compilation Error 💥
   |                     ^^^^^ value borrowed here after move
   |

Memory layout

The moment new_owner receives ownership of the value, the stack based pointer (incl len + capacity) gets removed from memory.

A move also occurs when we pass a variable to a function

fn main() {
    let book = String::from("Refactoring");

    give(book); // Gives away ownership

    // 💥 Compilation Error 💥
    println!("book no longer owns the book: {}", book);
}

fn give(book: String) {
    println!("i received ownership of the book: {}", book);
}

Returning a value from a function passes ownership to caller 💪

let owner = receive_book();

fn receive_book() -> String {
    String::from("The Clean Coder")
}

3. When the owner goes out of scope, the value gets dropped

fn main() {
    let book = String::from("The Clean Coder"); // book exists

    { // another scope starts

        // scoped_book exists
        let scoped_book = String::from("Cast away");

        // can access both here
        println!("{book} & {scoped_book}");

    } // scoped_book goes out of scope (aka drop)

    // here only book exists, scoped_book has been dropped!
    println!("{}", book);
} // book gets dropped

The moment the owner goes out of scope, memory gets deallocated immediately! The scope created by {} is actually a technique to have even more control over how long a value should exist.

Next

Who moved my value?

Previous

Kiss my default props