C.W.K.
Stream
Lesson 03 of 05 · published

The Borrowing Rules & the Borrow Checker

~12 min · borrowing, borrow-checker, nll

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

You've met the borrowing rules piecemeal. Let's state them cleanly and meet the part of the compiler that enforces them: the borrow checker.

The two rules

One: at any given time you can have either one mutable reference or any number of immutable references. Two: references must always be valid — they can never outlive the data they point at. That's the whole borrow checker, in two sentences. Everything else is the compiler proving your code obeys them.

References live until last use, not end of scope

Modern Rust is smart: a reference's borrow ends at its last use, not at the closing brace. So you can create immutable borrows, finish using them, and then take a mutable borrow in the same scope. This is called non-lexical lifetimes (NLL), and it's why a lot of code that looks like it should fail actually compiles.

If a borrow error confuses you, find the last use. The borrow lasts from its creation to its final use. Often the fix is just reordering: finish with the immutable references before you take the mutable one, and the conflict vanishes.

The borrow checker is a prover, not a linter

It doesn't guess or warn — it proves, for every reference, that the data outlives it and that the aliasing rules hold. If it can't prove safety, it rejects the code. That's why a Rust program that compiles has no use-after-free and no data races: not 'probably,' but proven.

Fighting it is a phase

Early on, you'll feel like you're fighting the borrow checker. Later, you realize it was catching real aliasing hazards you couldn't see. The rules don't change; your intuition catches up to them. That shift is the whole reason this quest frames the compiler as a mentor.

Code

Non-lexical lifetimes: last use ends the borrow·rust
fn main() {
    let mut v = vec![1, 2, 3];

    let first = &v[0];     // immutable borrow starts
    println!("{first}");   // ...and ends here, at its last use

    v.push(4);             // mutable borrow now allowed — no conflict
    println!("{v:?}");

    // If we used `first` AFTER v.push(4), THAT would be E0502:
    // the immutable borrow would still be alive during the mutation.
}

External links

Exercise

Write code that takes an immutable reference to a vector, uses it in a println, then mutates the vector afterward — and confirm it compiles thanks to NLL. Then move the println to *after* the mutation and watch the same code stop compiling. Explain why the borrow now overlaps.
Hint
NLL ends the immutable borrow at its last use. Move that use past the mutation and the borrow's lifetime now spans the mutation — that's the overlap E0502 reports.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.