Rust Ownership

Understanding Rust's memory management system

🔐 What is Ownership?

Ownership is Rust's unique system for managing memory safely without garbage collection. Each value has a single owner, and when the owner goes out of scope, the value is automatically cleaned up, preventing memory leaks and dangling pointers.


fn main() {
    let s1 = String::from("hello");  // s1 owns the string
    let s2 = s1;                     // ownership moves to s2
    // println!("{}", s1);           // Error! s1 no longer owns the value
    println!("{}", s2);              // This works
}
                                    

Output:

hello

Ownership Rules

Ownership Rules

👤

Single Owner

Each value has exactly one owner

let x = String::from("hello");
🔄

Move Semantics

Ownership can be transferred (moved)

let y = x; // ownership moves
🗑️

Automatic Cleanup

Memory freed when owner goes out of scope

} // x is dropped here
🚫

No Double Free

Prevents memory safety issues

// Compile-time safety

🔹 Ownership Transfer (Move)

When you assign a value to another variable, ownership moves:

fn main() {
    // Ownership with heap data
    let s1 = String::from("hello");
    let s2 = s1;  // s1's ownership moves to s2
    
    println!("{}", s2);  // Works
    // println!("{}", s1);  // Error: s1 no longer valid
    
    // Stack data is copied, not moved
    let x = 5;
    let y = x;  // x is copied to y
    
    println!("x: {}, y: {}", x, y);  // Both work
}

Output:

hello

x: 5, y: 5

🔹 Functions and Ownership

Passing values to functions also transfers ownership:

fn main() {
    let s = String::from("hello");
    
    takes_ownership(s);  // s's ownership moves into function
    // println!("{}", s);  // Error: s no longer valid
    
    let x = 5;
    makes_copy(x);       // x is copied, still valid
    println!("x is still: {}", x);  // This works
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string goes out of scope and is dropped

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer goes out of scope, nothing special happens

Output:

hello

5

x is still: 5

🔹 Returning Values and Ownership

Functions can return ownership to the caller:

fn main() {
    let s1 = gives_ownership();         // Function returns ownership
    let s2 = String::from("hello");     // s2 comes into scope
    let s3 = takes_and_gives_back(s2);  // s2 moves in, s3 gets ownership
    
    println!("s1: {}", s1);
    println!("s3: {}", s3);
    // s2 is no longer valid here
}

fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string  // Return ownership to caller
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string  // Return ownership to caller
}

Output:

s1: yours

s3: hello

🔹 Clone for Deep Copy

Use clone() to create a deep copy instead of moving:

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // Deep copy, both s1 and s2 are valid
    
    println!("s1: {}, s2: {}", s1, s2);
    
    // Both variables can be used
    let len1 = s1.len();
    let len2 = s2.len();
    
    println!("Lengths: {} and {}", len1, len2);
}

Output:

s1: hello, s2: hello

Lengths: 5 and 5

🧠 Test Your Knowledge

What happens when you assign a String to another variable in Rust?