lesson 3 of N · rust fluency

ownership

ownership is the core feature that lets rust guarantee memory safety without a garbage collector. it's the most famous part of the language, and the part you'll fight with most at first.

the three rules

ownership is governed by three strict rules. the compiler enforces these at compile time. if you break them, the code simply will not build.

  1. each value in rust has a variable that's called its owner.
  2. there can only be one owner at a time.
  3. when the owner goes out of scope, the value will be dropped.

move vs copy

what happens when you assign one variable to another? it depends on where the data lives.

stack-only data (copy)

simple types like integers (i32) are stored on the stack. they implement the Copy trait, so assigning them creates a full copy.

let x = 5;
let y = x; // x is still valid because it was copied
println!("{x}, {y}"); // works fine

heap data (move)

complex types like String are stored on the heap. they do NOT implement Copy. assigning one to another moves the ownership.

let s1 = String::from("hello");
let s2 = s1; // ownership of the data moved to s2
// println!("{s1}"); // ERROR: value borrowed here after move

in this case, s1 is now invalid. rust does this to prevent "double-free" errors where two variables try to clean up the same memory.

the "aha" moment:
think of ownership as a physical object. you can't be in two places at once. if you give your phone to a friend, you no longer have it. if you want it back, they have to give it back.

ownership and functions

passing a value to a function also moves ownership.

fn take_it(s: String) {
    println!("{s}");
} // s goes out of scope here and is dropped

fn main() {
    let my_str = String::from("hi");
    take_it(my_str); 
    // println!("{my_str}"); // ERROR: moved into function
}

if you want the function to give the value back, you must return it.

do it now

  1. create a new crate: cargo new ownership
  2. create a String, assign it to another variable, and try to print the first one.
  3. do the same with an i32 and notice it works.
  4. write a function that takes a String, and try to use that string after calling the function.

check yourself

primary source

read chapter 4.1 of the Rust Book, what is ownership?. this is the single most important page in the book.

this is where most people get frustrated. if the "move" logic doesn't click, ask. i can explain it with different metaphors.