On-Ramp to Rust

the shared versus exclusive borrow rule

Borrowing comes with one rule that does most of the safety work, and it is worth stating as plainly as possible: at any given moment, for a given piece of data, you may have either any number of shared (read-only) borrows, or exactly one mutable (read-write) borrow — but never both at the same time. People shorten this to one mutable XOR many shared, or aliasing XOR mutability. Many readers, fine. One writer, fine. A writer at the same time as any other reader or writer, forbidden.

Why such a sharp rule? Because almost every nasty data bug comes from one part of the code changing something while another part is still looking at it. In C this is the source of subtle aliasing bugs and, across threads, of data races where two threads touch the same memory and at least one writes, with no ordering between them. Rust kills this whole class by construction. A shared reference &T promises the borrowed data will not change while you hold it, which is exactly why it is safe to have many of them. A mutable reference &mut T promises you are the only one who can see this data right now, which is exactly why it is safe to let you change it. Because the two cannot coexist, no reader can ever observe a half-finished write, and no two writers can clobber each other. This is the same guarantee for single-threaded code (no surprise aliasing) and for multi-threaded code (no data races).

Concretely, the compiler tracks the live borrows of each value. Take let r1 = &v; let r2 = &v; — two shared borrows, allowed. Take let m = &mut v; — one mutable borrow, allowed, but only if no shared borrow of v is still alive. Try to make a &mut v while a &v is still in use and the compiler refuses: cannot borrow v as mutable because it is also borrowed as immutable. It feels strict at first, and beginners do fight it. But the rule is not arbitrary bureaucracy — it is the precise condition under which the compiler can guarantee, with no runtime checks and no garbage collector, that your references never see corrupted or freed data.

let mut v = vec![1, 2, 3]; let a = &v; // shared borrow let b = &v; // another shared borrow -- fine println!("{a:?} {b:?}"); let m = &mut v; // exclusive borrow -- OK now (a, b no longer used) m.push(4); // let c = &v; // ERROR if placed while m is still alive

Many shared borrows coexist; a mutable borrow demands it is the only borrow alive.

This rule is the compile-time version of why data races happen: a writer overlapping any other access is exactly the dangerous case, and Rust forbids it in safe code. It applies within one thread too, not only across threads.

Also called
aliasing XOR mutabilityone mutable or many shared&T vs &mut T共享 XOR 可變一可變或多共享