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 21: The Borrow Checker, How the Compiler Thinks

You will understand

  • Where borrow checking runs in the compiler pipeline
  • How to simulate borrow errors mentally
  • What E0382, E0502, and E0505 really mean

Reading time

45 min
+ 25 min exercises
Compiler Pipeline

Where the Borrow Checker Runs

Source surface Rust syntax AST parsed structure HIR desugared, name resolved MIR control-flow aware moves, drops, temps borrow check LLVM IR lower-level optimizer input Binary machine code
Borrow checking happens on MIR because MIR makes liveness, control flow, drops, and temporaries explicit. The compiler is not arguing with your pretty syntax; it is reasoning over a lowered control-flow model.
Worksheet

How to Simulate a Borrow Error

1. List owners v owns Vec buffer 2. Mark borrows and last use `first = &v[0]` alive until last print 3. Check conflicting overlap ❌ shared + mutable overlap
Error Decoder Cards

What the Compiler Is Really Telling You

E0382 use after move E0502 shared and mutable conflict E0505 move while borrowed E0515 returning dangling reference E0521 borrow escapes closure/body
E0382
use of moved value
You attempted to use a binding after its ownership was transferred. The compiler statically tracks every move and invalidates the original binding at that point. The moved-from name still exists in the source text but has no authority.
Borrow instead of moving: &s1. Or restructure so you use s1 before the move. Or call .clone() if you genuinely need two independent copies.
E0502
cannot borrow as mutable because it is also borrowed as immutable
A shared reference (&T) is alive while you try to take an exclusive reference (&mut T). Aliasing XOR mutation: the compiler refuses to let both exist simultaneously because the mutable borrow could invalidate what the shared reference sees.
Shorten the shared borrow's lifetime — move its last use before the mutable borrow. NLL (Non-Lexical Lifetimes) makes this easier: a reference dies at its last use, not at scope end.
E0505
cannot move out of value because it is borrowed
A reference still points into a value you are trying to move (transfer ownership). Moving would invalidate the reference, creating a dangling pointer — exactly what Rust's borrow checker exists to prevent.
Ensure no references are alive at the point of the move. Restructure the code so borrows end before ownership transfer, or use .clone() to make the reference independent.
#![allow(unused)]
fn main() {
let mut data = String::from("hello");
let r = &data;               // borrow starts
data.push_str(" world");     // E0502: &mut while &data lives
println!("{r}");             // borrow extends to here
}
Borrow starts MIR records r as a live shared borrow of data.
E0502: conflict push_str requires &mut data but r holds &data. The borrow checker rejects.
NLL liveness Borrow of r ends at its last use (line 4), not at scope end. Moving println! above push_str would fix it.

Readiness Check - Borrow Checker Mental Simulation

SkillLevel 0Level 1Level 2Level 3
Trace ownership and borrowsI only react to error textI can identify owner and referencesI can mark borrow start and last-use pointsI can predict likely errors before compiling
Decode compiler diagnosticsI copy fixes blindlyI can interpret one common errorI can map multiple errors to one root conflictI can choose minimal structural fixes confidently
Restructure conflicting codeI use random clones/movesI can fix simple overlap conflictsI can refactor borrow scopes intentionallyI can design APIs that avoid borrow friction by construction

Target Level 2+ before advancing into larger async/concurrency ownership scenarios.

Compiler Error Decoder - Borrow Checker Core

Error codeWhat it usually meansTypical fix direction
E0502Shared and mutable borrow overlapEnd shared borrow earlier or split scope before mutable operation
E0505Move attempted while borrowedReorder to end borrow first, or clone if independent ownership is required
E0515Returning reference to local/temporary dataReturn owned value or borrow from caller-provided input

Use one worksheet for every failure: owner, borrow region, conflicting operation, smallest safe rewrite.

Step 1 - The Problem