Back to Rust

Ownership and Borrowing Explained

October 5, 202510 min read

Ownership and Borrowing Explained

Rust's ownership system is its most distinctive feature, enabling memory safety without garbage collection.

The Three Rules of Ownership

  1. Each value has a single owner
  2. When the owner goes out of scope, the value is dropped
  3. Values can be moved or borrowed

Understanding Moves

fn main() {
    let s1 = String::from("hello");
    let s2 = s1; // s1 is moved to s2
    
    // println!("{}", s1); // Error! s1 is no longer valid
    println!("{}", s2); // OK
}

When s1 is assigned to s2, ownership moves. This prevents double-free errors.

Borrowing: Shared References

Instead of moving, we can borrow:

fn calculate_length(s: &String) -> usize {
    s.len()
} // s goes out of scope but doesn't own the data

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("{} has length {}", s1, len); // s1 still valid!
}

The & creates a reference that borrows the value without taking ownership.

Mutable Borrowing

For mutations, use &mut:

fn append_world(s: &mut String) {
    s.push_str(", world");
}

fn main() {
    let mut s = String::from("hello");
    append_world(&mut s);
    println!("{}", s); // "hello, world"
}

The Borrowing Rules

  • Any number of immutable references (&T)
  • OR exactly one mutable reference (&mut T)
  • References must always be valid

This prevents data races at compile time!

Common Patterns

Pattern 1: Multiple Readers

let data = vec![1, 2, 3];
let r1 = &data;
let r2 = &data;
println!("{:?} {:?}", r1, r2); // OK

Pattern 2: Single Writer

let mut data = vec![1, 2, 3];
let r = &mut data;
r.push(4);
// Can't create another reference while r exists

Why This Matters

Ownership and borrowing eliminate entire classes of bugs:

  • Use-after-free
  • Double-free
  • Data races
  • Null pointer dereferences

All caught at compile time with zero runtime overhead.

Conclusion

The ownership system might seem restrictive at first, but it's Rust's superpower—enabling fearless concurrency and memory safety without garbage collection.