Borrowing & References
Chapter 3 ended with a real frustration: passing a value to a function moves it, leaving the caller unable to use it afterward. Borrowing is the fix — letting a function use a value without taking ownership of it at all.
References With &
&s1 creates a reference to s1 rather than moving it — calculate_length can read the string, but never owns it. Once the function returns, s1 is exactly as valid as before, solving Chapter 3's function-ownership problem directly.
Mutable References With &mut
&mut lets a function genuinely modify the borrowed value — but this power comes with a strict rule.
The Borrow Checker's Rules
- At any given time, you can have either any number of immutable references (
&) or exactly one mutable reference (&mut) — never both at once. - References must always be valid — no reference to something that's already been dropped.
Both rules are enforced entirely at compile time.
| Situation | Allowed? |
|---|---|
Two immutable references (&s, &s) at once | Yes |
One mutable reference (&mut s) alone | Yes |
| One mutable + one immutable reference, both active at once | No — compile error |
| Two mutable references, both active at once | No — compile error |
Why These Rules Exist
The mutable-XOR-immutable rule prevents a real, classic bug class: something reading data while something else modifies it underneath it. Concretely — if two mutable references to a growing collection existed simultaneously, one holder could trigger a reallocation (the collection outgrowing its current memory and moving elsewhere) while the other still points at the old, now-freed memory location — a genuine dangling-pointer bug that has caused real, exploitable vulnerabilities in C++. Rust's borrow checker makes this class of bug simply not compile, rather than something to carefully avoid at runtime. This is also the foundation of Rust's "fearless concurrency" (Course 2) — the exact same rule that prevents this single-threaded aliasing bug also prevents data races between threads.
Dangling References
In C or C++, this same pattern compiles and produces a genuine dangling pointer — undefined behavior waiting to happen at runtime, possibly much later and far from where the actual mistake was made. In Rust, it's simply a compile error: the borrow checker sees that s is dropped at the end of the function, and refuses to let a reference to it escape.
Rust's Alternative to Go's GC
The full arc from Chapter 1 is now complete. Go's garbage collector scans memory at runtime to find and free data nothing references anymore — genuinely useful, but with real runtime overhead and pause times that are hard to predict exactly when they'll land. Rust's borrow checker instead analyzes ownership and borrowing entirely at compile time — by the time a Rust program actually runs, every memory-safety guarantee has already been proven, with zero runtime cost for that safety, no background process ever running, and the deterministic drop timing Chapter 3 introduced.
| Rule | What It Prevents |
|---|---|
| One owner at a time (Ch.3) | Ambiguity about who's responsible for freeing memory |
| Mutable XOR immutable references | Reading data while it's being modified (aliasing bugs, data races) |
| References must stay valid | Dangling references / use-after-free |
Coding Challenges
Write a function count_words that takes a &String and returns the number of words (split_whitespace().count()) without taking ownership. Call it twice on the same variable to prove it remains valid after each call.
📄 View solutionWrite code that creates one mutable String, then attempts to hold both an immutable reference and a mutable reference to it at the same time (using both before the function ends). Run cargo run, note the exact compiler error, then fix it by ensuring the immutable reference's last use happens before the mutable one is created.
📄 View solutionExplain, in your own words, why Rust's approach to memory safety has zero runtime cost compared to Go's garbage collector — referencing specifically when each language's safety mechanism actually does its work.
📄 View solutionChapter 4 Quick Reference
- &value — an immutable reference; the function can read but not modify, and never takes ownership
- &mut value — a mutable reference; allows modification, but only one may exist at a time
- Borrow rule: any number of
&references, OR exactly one&mutreference — never both simultaneously - References must stay valid — the compiler rejects any reference that could outlive the data it points to
- These rules prevent aliasing bugs and data races at compile time — the same foundation behind Rust's "fearless concurrency"
- Rust vs. Go's GC: Rust proves memory safety at compile time (zero runtime cost); Go's GC checks at runtime (real, ongoing overhead)
- Non-lexical lifetimes: a reference's effective scope ends at its last use, not the closing brace — full lifetime syntax is Course 2's territory
- Next chapter: Structs & Methods — struct definitions, impl blocks, and methods vs. associated functions