Absolutely. The ownership model is the heart of Rust’s memory safety without garbage collection. It eliminates memory bugs at compile timeβ€”not by runtime checks, but by enforcing strict rules about how data is accessed and transferred.

Let’s break down the three core concepts: Move, Copy, and Borrowβ€”with clear examples and practical implications.


πŸ”‘ Core Principle:

Each piece of data has exactly one owner. When the owner goes out of scope, the data is automatically cleaned up.

This prevents memory leaks and use-after-free bugs by design.


1. πŸ”„ Move β€” Transfer of Ownership

In Rust, assignment transfers ownership by default (unlike C++ or Go). The original variable is invalidated.

Example:

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1; // πŸ‘‰ `s1` is MOVED to `s2`

    // println!("{}", s1); // ❌ COMPILE ERROR!
    // "value borrowed here after move"
    
    println!("{}", s2); // βœ… OK
}

Why?
String owns heap-allocated data. If both s1 and s2 pointed to it, both would try to free it β†’ double-free crash.
Rust prevents this by invalidating s1 on move.

βœ… Safety: No dangling pointers. No double-frees.
⚠️ Effect: You can’t accidentally use stale data.


2. πŸ“‹ Copy β€” Cheap, Implicit Duplication

Some types are automatically copied on assignment (instead of moved) because they’re cheap and live entirely on the stack.

These types implement the Copy trait (e.g., integers, booleans, floats, tuples of Copy types).

Example:

fn main() {
    let x = 5;
    let y = x; // πŸ‘‰ `x` is COPIED (not moved)

    println!("x = {}, y = {}", x, y); // βœ… Both work!
}

Why is i32 copied but String moved?

  • i32 is just 4 bytes on the stack β†’ cheap to duplicate.

  • String owns a pointer to heap data β†’ expensive and unsafe to duplicate implicitly.

βœ… You never have to call .clone() for simple values.
🚫 You cannot implement Copy for types that manage resources (files, sockets, heap memory).


3. 🀝 Borrow β€” Temporary Access Without Ownership

Instead of moving data (which makes the original unusable), you can borrow it using references (&T for read, &mut T for write).

Immutable Borrow (Read-Only)

fn main() {
    let s = String::from("Karachi");
    let len = calculate_length(&s); // πŸ‘‰ Borrow `s` (no move!)
    
    println!("Length of '{}' is {}", s, len); // βœ… `s` still valid
}

fn calculate_length(s: &String) -> usize {
    s.len() // `s` is a reference β€” doesn’t own the data
} // `s` goes out of scope here, but the *owner* still owns it

Mutable Borrow (Read-Write)

fn main() {
    let mut s = String::from("Hello");
    change(&mut s); // πŸ‘‰ Mutable borrow
    println!("{}", s); // "Hello, Rust!"
}

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

πŸ”’ Borrowing Rules (Enforced at Compile Time!)

  1. You can have either:

    • One or more immutable references (&T)

    • OR exactly one mutable reference (&mut T)

  2. References must always be valid (no dangling pointers).

❌ This fails to compile:

let mut s = String::from("test");
let r1 = &s;
let r2 = &s;
let r3 = &mut s; // ❌ Can't borrow mutably while immutable refs exist
println!("{}, {}, {}", r1, r2, r3);

πŸ’‘ Practical Implications for Reliable Code

Problem in C/Go Rust’s Solution
Accidentally using freed memory Move semantics ensure only one owner
Unintended data mutation Immutable by default; mutable borrows are explicit and exclusive
Iterator invalidation during loop Borrow checker rejects code that modifies a collection while iterating
Data races in threads Same rules apply across threads β†’ data races are compile-time errors

πŸ› οΈ When to Use What?

Scenario Use
Passing a String to a function that needs full control Move (take_ownership(s))
Just reading a value (e.g., logging, computing length) Immutable borrow (log(&s))
Modifying data in-place (e.g., appending, sorting) Mutable borrow (append(&mut vec))
Working with integers, booleans, small structs Copy (automatic)
Need a deep clone (e.g., fork data for parallel processing) Explicit .clone()

βœ… Summary

Concept What It Does Safety Benefit
Move Transfers ownership; original is invalidated Prevents double-free, use-after-free
Copy Duplicates simple stack values Zero-cost, no ownership hassle
Borrow Temporary access via &T or &mut T Prevents data races, iterator invalidation, dangling pointers

πŸ¦€ The borrow checker is your allyβ€”it’s strict early so you never debug memory corruption at 3 AM.


Β 

Last modified: Saturday, 8 November 2025, 11:34 AM