Rust Fundamentals: Ownership, Errors and Fearless Concurrency · Rust Basics and Ownership

Ownership: Rust's Superpower

Every value has one owner; when the owner goes out of scope the value is freed. Assigning or passing a String moves it, Copy types are duplicated, and clone makes an explicit deep copy. This is how Rust is memory-safe without a garbage collector.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A small playlist program that moves, copies and clones songs between variables and functions, so you can see exactly when a value can and can't be used.

The idea

Most languages either make you free memory yourself (C, C++) or use a garbage collector (Java, Go, Python). Rust uses ownership, checked at compile time:

  1. Each value has exactly one owner (a variable).
  2. When the owner goes out of scope, the value is dropped (freed).
  3. Assigning a heap value like String or Vec to another variable, or passing it to a function, moves ownership. The old variable can no longer be used.

Simple fixed-size types (integers, floats, bool, char, and tuples of them) implement Copy: assigning them copies the bits, and both variables stay usable.

If you really want two independent heap values, call .clone(): an explicit, visible (and possibly expensive) deep copy.

Functions can give ownership back by returning a value. Passing ownership in and out everywhere is clumsy, though, which is why the next lesson introduces borrowing.

The code

Moves, copies and clones
fn describe(song: String) -> String {
    // `song` is owned here; we return a new String (ownership of it goes to the caller)
    format!("{} ({} chars)", song, song.len())
}

fn total_seconds(minutes: u32, seconds: u32) -> u32 {
    minutes * 60 + seconds // u32 is Copy: callers keep their values
}

fn main() {
    let favourite = String::from("Blue in Green");
    let moved = favourite;               // ownership moves to `moved`
    // println!("{favourite}");          // error: value used after move
    println!("moved: {moved}");

    let copy_a = 5;
    let copy_b = copy_a;                 // integers are Copy: both usable
    println!("copies: {copy_a} and {copy_b}");

    let original = String::from("So What");
    let cloned = original.clone();       // explicit deep copy: two independent Strings
    println!("original: {original}, clone: {cloned}");

    let summary = describe(cloned);      // `cloned` moves into the function...
    println!("summary: {summary}");      // ...and we get a new String back
    // println!("{cloned}");             // error: `cloned` was moved into describe()

    let (m, s) = (9, 25);
    println!("length: {}s, and m and s are still usable: {m}:{s}", total_seconds(m, s));

    {
        let temporary = String::from("dropped at the end of this block");
        println!("{temporary}");
    } // `temporary` goes out of scope here and its memory is freed automatically
}

Output

moved: Blue in Green
copies: 5 and 5
original: So What, clone: So What
summary: So What (7 chars)
length: 565s, and m and s are still usable: 9:25
dropped at the end of this block

Run this on your computer — see “Get Started” for setup.

The commented-out lines are exactly what the compiler would reject, with errors like "borrow of moved value: favourite". Those errors are Rust stopping use-after-free and double-free bugs before your program runs.

Common mistakes

  • Using a value after moving it. If you still need it, borrow it (next lesson) or clone() it.
  • Cloning to silence the compiler. It works, but extra copies cost time and memory; first ask whether a borrow would do.
  • Expecting String to be Copy. Only fixed-size, stack-only types are Copy.
  • Thinking moves are expensive. A move copies a small pointer/length/capacity record, not the heap data.

Key points

  • One owner per value; the value is dropped when its owner goes out of scope.
  • Assigning or passing a String/Vec moves it; the old variable becomes unusable.
  • Copy types (numbers, bool, char) are duplicated instead of moved.
  • .clone() makes an explicit deep copy.

Further reading: What is ownership?

Practice questions

What are Rust's three ownership rules?

Answer

Each value has an owner; there can be only one owner at a time; when the owner goes out of scope, the value is dropped.

Why can you use an integer after assigning it to another variable but not a String?

Answer

Integers implement Copy, so assignment duplicates the value. String owns heap memory and isn't Copy, so assignment moves ownership and the original variable can't be used (preventing a double free).

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?