Memory Safety

Understanding Rust Ownership: The Key to Memory Safety

5 min read

Rust's ownership system is one of its most distinctive features. It enables Rust to guarantee memory safety without needing a garbage collector. In this post, we'll explore how ownership works and why it's so powerful.

What is Ownership?

Ownership is a set of rules that governs how Rust manages memory. The core rules are:

The Three Rules of Ownership

  1. 1 Each value in Rust has an owner
  2. 2 There can only be one owner at a time
  3. 3 When the owner goes out of scope, the value is dropped

A Simple Example

Let's look at a basic example:

main.rs
fn main() {
    let s = String::from("hello");
    println!("{}", s);
}

In this code, s is the owner of the String value. When s goes out of scope at the end of main, Rust automatically calls the drop function and the memory is freed.

Move Semantics

When you assign one variable to another, Rust moves the ownership:

main.rs
fn main() {
    let s1 = String::from("hello");
    let s2 = s1; // s1 is moved to s2

    // println!("{}", s1); // This would cause a compile error!
    println!("{}", s2); // This works fine
}

This prevents double-free errors that plague C and C++ programs.

Borrowing

If you want to use a value without taking ownership, you can borrow it:

main.rs
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

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

Mutable References

You can also have mutable references, but with restrictions:

main.rs
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}

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

The key restriction: you can have either one mutable reference OR any number of immutable references, but not both at the same time.

Why This Matters

Rust's ownership system prevents:

  • Dangling pointers: References that point to freed memory
  • Double frees: Freeing the same memory twice
  • Data races: Multiple threads accessing the same data without synchronization

Key insight

All of these checks happen at compile time, with zero runtime overhead. You get the safety of a garbage-collected language with the performance of manual memory management.

Conclusion

Ownership is the foundation of Rust's memory safety guarantees. While it takes some getting used to, it eliminates entire classes of bugs that are common in other systems programming languages.

In future posts, we'll explore more advanced ownership patterns and how they enable safe concurrent programming.