Rust Ownership

🦀 Rust Ownership

Ownership is the most important concept in Rust.

It is the reason Rust is memory-safe, fast, and does not need a garbage collector.

If you understand Ownership, you understand the core of Rust.


🧠 What Is Ownership?

Ownership is a set of rules that the Rust compiler uses to manage memory.

It decides:

  • Who owns a value

  • When memory should be freed

  • How to prevent memory bugs (null pointers, data races, use-after-free)

All checks happen at compile time, not runtime.


🔑 The 3 Ownership Rules

✅ Rule 1: Each value has one owner

let s = String::from("Rust");

Here:

  • "Rust" is stored on the heap

  • s is the owner of that memory


✅ Rule 2: Only one owner at a time

let s1 = String::from("Hello");
let s2 = s1; // ownership moved

Now:

  • Ownership moves from s1 to s2

  • s1 becomes invalid

// println!("{}", s1); ❌ Error
println!("{}", s2); // ✅ OK

This prevents double free errors.


✅ Rule 3: When the owner goes out of scope, memory is freed

{
let s = String::from("Rust");
} // s goes out of scope → memory freed automatically

✔ No memory leaks
✔ No manual free()
✔ No garbage collector


🔹 Stack vs Heap (Why Ownership Exists)

Stack Heap
Fixed size Dynamic size
Fast Slower than stack
Simple values Complex values
let x = 10; // stored on stack
let s = String::from("Hi"); // stored on heap

Ownership mainly applies to heap data.


🔹 Move vs Copy

▶️ Copy Types (Ownership Not Moved)

let a = 5;
let b = a;
println!(“{}”, a); // ✅
println!(“{}”, b);

Because i32 is a Copy type.

Common Copy types:

  • Integers (i32, u64, etc.)

  • Floating-point numbers

  • bool

  • char

  • Tuples (if all elements are Copy)


▶️ Move Types (String, Vec, etc.)

let s1 = String::from("Rust");
let s2 = s1; // move
// println!(“{}”, s1); ❌ Error
println!(“{}”, s2);

Heap data cannot be copied automatically, so ownership is moved.


🔹 Ownership and Functions

▶️ Passing Ownership to a Function

fn take_ownership(s: String) {
println!("{}", s);
}
fn main() {
let name = String::from(“Rust”);
take_ownership(name);
// println!(“{}”, name); ❌ Error
}

Ownership moves into the function.


▶️ Returning Ownership from a Function

fn give_ownership() -> String {
String::from("Rust")
}
fn main() {
let s = give_ownership();
println!(“{}”, s);
}

Ownership is transferred back.


🔹 Borrowing (Ownership Without Transfer)

To avoid moving ownership, Rust uses borrowing.

fn show(s: &String) {
println!("{}", s);
}
fn main() {
let name = String::from(“Rust”);
show(&name);
println!(“{}”, name); // ✅ still valid
}

✔ Ownership stays with name
✔ Function only borrows the value


🔹 Mutable Borrowing Rules (Preview)

let mut s = String::from("Hello");

let r1 = &mut s;
// let r2 = &mut s; ❌ not allowed

r1.push_str(” Rust”);

Rules:

  • You can have multiple immutable references, OR

  • One mutable reference

  • Not both at the same time

This prevents data races.


❌ Common Beginner Mistakes

  • Treating String like an integer

  • Forgetting ownership moves

  • Trying multiple mutable references

  • Ignoring compiler error messages

👉 Rust compiler errors are very helpful, not enemies.


🧠 Ownership Summary

Concept Meaning
Owner Variable that controls memory
Move Ownership transfer
Copy Value duplicated
Scope end Memory freed
Borrow Temporary access without ownership

You may also like...