Chapter 11: Borrowing and References, First Contact
Prerequisites
You will understand
&Tvs&mut T— shared vs exclusive- Why references cannot outlive their owner
- How NLL shortened borrow lifetimes
Reading time
25 min
+ 15 min exercises
Builds on Chapter 10
Borrowing exists because of ownership — a reference borrows the owner's data without taking ownership.
Revisit Ch 10 →
Borrow Timeline
Many Readers or One Writer
Reference Memory Diagram
A Reference Points Into an Existing Ownership Story
Rules Card
The Two Borrowing Invariants
#![allow(unused)]
fn main() {
let mut data = vec![1, 2, 3];
let r1 = &data; // shared borrow
let r2 = &data; // another shared borrow
println!("{r1:?} {r2:?}"); // last use of r1, r2
data.push(4); // mutable access OK — borrows ended
}
Owner
data owns the Vec on the heap.
&T shared borrows
r1 and r2 borrow data immutably. Multiple readers allowed.
NLL ends borrows here
Non-Lexical Lifetimes: borrows end at last use, not block end.
&mut T access
push requires &mut self. Valid because shared borrows already ended.
In Your Language: References vs Pointers
Rust — borrowing
#![allow(unused)]
fn main() {
fn len(s: &String) -> usize {
s.len() // borrow — no ownership transfer
}
let owned = String::from("hi");
let n = len(&owned); // owned is still valid
}
Java — everything is a reference
int len(String s) {
return s.length(); // s is a reference (always)
}
String owned = "hi";
int n = len(owned); // works — GC manages lifetime
// But: anyone could mutate s in Java
Compiler Error Decoder - Borrowing Basics
These are the top errors learners hit in early borrowing code.
| Error code | What it usually means | Typical fix direction |
|---|---|---|
| E0502 | Mutable and immutable borrows overlap | End shared borrows earlier, split scopes, or reorder operations |
| E0499 | More than one mutable borrow exists at once | Keep one &mut alive at a time; refactor into sequential mutation steps |
| E0596 | Tried to mutate through an immutable reference | Change to &mut, or move mutation to the owner |
When debugging, first mark where each borrow starts and where its last use occurs. Most fixes come from shrinking one overlapping region.