Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 11: Borrowing and References, First Contact

Prerequisites

You will understand

  • &T vs &mut T — shared vs exclusive
  • Why references cannot outlive their owner
  • How NLL shortened borrow lifetimes

Reading time

25 min
+ 15 min exercises
Borrow Timeline

Many Readers or One Writer

Valid Rejected value lifetime value lifetime &T borrow r1 &T borrow r2 &mut borrow w ✓ shared borrows overlap safely ✓ writer starts after readers end &T borrow r1 still live &mut borrow w ❌ overlap E0502: cannot mutably borrow while shared borrow is live NLL insight: borrows end at last use, not always block end
Borrowing is not “pointers with better manners.” It is a time-structured access contract. The timeline is the right mental tool because the question is always whether access regions overlap in a forbidden way.
Reference Memory Diagram

A Reference Points Into an Existing Ownership Story

STACK HEAP s1: String ptr: 0x1000 len: 5 cap: 5 r1: &s1 borrow only h e l l o r1 points into an existing owner path. Borrowing does not create a second owner.
A reference is meaningful only inside the ownership graph it borrows from. That is why “references are just pointers” is the wrong model. The pointer shape may exist, but the aliasing and validity contract is what makes it a Rust reference.
Rules Card

The Two Borrowing Invariants

Rule 1 — Aliasing XOR Mutation Many &T readers XOR One &mut T writer Never both at the same time Rule 2 — No Dangling References r: &T DROPPED A reference must never outlive the value it borrows from Compiler rejects dangling at compile time
These two rules are the entire borrowing system. Every borrow checker error traces back to one of them. Learn to identify which rule is being violated and the error message becomes a diagnosis, not a mystery.
#![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 codeWhat it usually meansTypical fix direction
E0502Mutable and immutable borrows overlapEnd shared borrows earlier, split scopes, or reorder operations
E0499More than one mutable borrow exists at onceKeep one &mut alive at a time; refactor into sequential mutation steps
E0596Tried to mutate through an immutable referenceChange 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.

Step 1 - The Problem