Chapter 20: Rust Ownership
Rust — Ownership.
If you understand ownership + borrowing + lifetimes, then 80–90% of “why Rust feels strict but safe” suddenly makes sense. Many people struggle with it for 1–4 weeks… then one day it clicks and they never want to go back to languages without it.
I’m going to explain it like we’re sitting together with chai, slowly, with lots of small examples you can run right now, real analogies from everyday life in Hyderabad, and building from very simple → medium → advanced.
1. The Three Core Rules of Ownership (Memorize These!)
Rust enforces exactly three rules at compile time — no exceptions:
- Each value has a single owner (Only one variable “owns” the data at any moment)
- When the owner goes out of scope, the value is dropped (Memory is automatically freed — no garbage collector, no manual free())
- There can be either:
- Many immutable borrows (&T) at the same time, OR
- Exactly one mutable borrow (&mut T) at a time (Never both at once — this prevents data races)
These rules are checked by the borrow checker (part of the compiler) — if code breaks any rule → compile error (not runtime crash).
2. Simple Example – Move Semantics (Rule 1 & 2)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fn main() { let s1 = String::from("Hyderabad"); // s1 owns the String on heap let s2 = s1; // Ownership MOVED to s2 // println!("s1 is {}", s1); // ERROR! s1 no longer owns anything println!("s2 is {}", s2); // Works — s2 is now the owner } // ← here s2 goes out of scope → String is dropped (memory freed) |
Analogy: You have one plate of biryani. You give the whole plate to your friend (s2 = s1). Now you don’t have the plate anymore — you can’t eat from it (s1 invalid). When your friend finishes dinner (goes out of scope), the plate is cleaned automatically.
This is called a move — cheap (just pointer transfer), no deep copy.
3. Copy vs Move – When Does It Copy Instead?
Types that implement Copy trait are copied instead of moved.
Most simple types are Copy:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fn main() { let x = 42; // i32 implements Copy let y = x; // copied — x still valid println!("x = {}, y = {}", x, y); // both 42 let name = "Webliance"; // &str is Copy (just a pointer + length) let name2 = name; println!("{} and {}", name, name2); // both work } |
But heap-allocated types like String, Vec, most structs → move by default.
4. Borrowing – Read Without Taking Ownership (Rule 3)
We use & to borrow (reference) instead of moving.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fn main() { let s = String::from("Namaste"); let len = calculate_length(&s); // borrow immutable — s still valid println!("The string '{}' has length {}", s, len); } fn calculate_length(s: &String) -> usize { s.len() // can read, but cannot mutate } |
- &s = immutable borrow (read-only reference)
- Function gets a view — does not take ownership
- Multiple immutable borrows OK at same time
5. Mutable Borrowing – Change Without Moving
Use &mut — but only one at a time.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fn main() { let mut s = String::from("Hello"); change(&mut s); // give mutable borrow println!("After change: {}", s); } fn change(s: &mut String) { s.push_str(", world!"); // can mutate because &mut } |
But this fails:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fn main() { let mut s = String::from("Hello"); let r1 = &mut s; // first mutable borrow let r2 = &mut s; // second mutable borrow — ERROR! // println!("{}, {}", r1, r2); } |
Compiler error: “cannot borrow s as mutable more than once at a time”
→ Prevents data races at compile time (even in single-threaded code!)
6. Mixing Immutable & Mutable Borrows (Classic Gotcha)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fn main() { let mut s = String::from("Rust"); let r1 = &s; // immutable borrow let r2 = &s; // another immutable — OK // let r3 = &mut s; // mutable borrow while immutables exist → ERROR! println!("{}, {}", r1, r2); } |
But this is OK:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fn main() { let mut s = String::from("Rust"); let r1 = &s; // immutable println!("{}", r1); // use it let r2 = &mut s; // mutable borrow — OK because r1 no longer used r2.push_str(" is awesome!"); } |
→ Borrows are tracked until last use, not until end of scope
7. Dangling References – Prevented Forever
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
fn main() { let r; // declare reference { let s = String::from("short lived"); r = &s; // borrow s } // s dropped here // println!("{}", r); // ERROR! r would dangle } |
Compiler catches: “borrowed value does not live long enough”
8. Functions + Ownership Summary (Quick Reference)
| Signature | What happens to argument | Can mutate? | Caller still owns after call? |
|---|---|---|---|
| fn take(s: String) | Moves ownership | Yes | No |
| fn read(s: &String) | Immutable borrow | No | Yes |
| fn mutate(s: &mut String) | Mutable borrow | Yes | Yes |
| fn return_string() -> String | Creates new owned value | – | Caller owns it |
Teacher Summary – The Big Picture
Rust Ownership =
- Every value has exactly one owner
- Owner going out of scope → value dropped (automatic cleanup)
- You can move ownership (cheap transfer)
- Or borrow (& read-only, &mut read-write)
- Only one mutable borrow OR many immutable borrows at once
- Compiler enforces all of this → no null pointers, no use-after-free, no data races
Analogy for Hyderabad: Ownership = you own one autorickshaw. Move = you give the whole rickshaw to your friend (you lose it). Borrow immutable = friend takes photo of your rickshaw (you still have it). Borrow mutable = friend drives your rickshaw for a while (only one can drive at a time).
Once you love this system, Rust feels liberating — you write code that can’t crash in memory-unsafe ways.
Practice today:
- Try moving a String → see error when using old variable
- Write function that borrows &str vs takes String
- Create mutable borrow conflict → read the compiler message (it’s helpful!)
- Return a &str from function — see lifetime errors if you try to return local
Want next?
- Lifetimes (next logical step after ownership & borrowing)?
- References & borrowing patterns in detail?
- Or structs + ownership inside them?
Just tell me — your Rust teacher from Telangana is ready! 🦀🚀
