Borrow-Checker Error Scenarios
Learn to read borrow-checker errors: they cite conflicting borrows and suggest fixes. Most resolve by shortening borrow scope, cloning at boundaries, or restructuring ownership.
Recipe
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
Fix pattern: end immutable borrow before mutable borrow (narrow scope, clone, or restructure).
Working Example
Problem: holding reference while mutating container.
// Before (error)
let r = &vec[0];
vec.push(1); // invalidates r
println!("{r}");
// After
let first = vec[0];
vec.push(1);
println!("{first}");Split borrows:
let (left, right) = slice.split_at_mut(mid);
// use left and right separatelyWhat this demonstrates:
- Reallocation invalidates references (
Vec::push) split_at_mutfor two mutable regions- Clone key at spawn boundary for async tasks
Deep Dive
Common errors: E0502 (borrow conflict), E0499 (two mut borrows), E0597 (does not live long enough). rust-analyzer often shows graph of borrows.
Gotchas
- Fighting with nested scopes - enlarge function. Fix: extract function taking
&T. - RefCell runtime borrow - panics instead of compile errors. Fix: shorten
borrow_mutscope. - Iterator holds borrow - collect or scope block ends before mutating vec.
- Struct fields overlapping - borrow whole struct. Fix: split fields into substructs.
- Async self borrow -
&mut selfacross await. Fix: owned data in task.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
Arc<Mutex<T>> | shared mutation | single-thread hot path |
| Index not reference | vec[i] copy | large non-Copy item |
| Arena indices | graph aliasing | simple tree |
FAQs
Read full error?
Second note shows where other borrow started.
too many clones?
Restructure after fixing correctness first.
polonius?
Future borrow checker may accept more; still write clear ownership.
learn without pain?
Small exercises; compiler as teacher.
IDE help?
rust-analyzer inlay hints and fix suggestions.
common in axum?
State clone Arc; don't hold & across await from handler self.
hashmap get_mut + insert?
Split scopes; cannot hold get while inserting same map.
closure captures?
Move closure or clone before move.
reference in struct?
Lifetime parameters required; see lifetime scenarios.
when unsafe?
Last resort; document invariants.
Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.