Skip to content
Select theme

What are the rules of borrowing?

Short answer: Two rules cover every borrow you will write. At any moment, a value has either any number of immutable borrows or exactly one mutable borrow — never both at the same time. And a borrow must always be valid: the value has to outlive the reference.

From a fresh project — cargo new borrow-rules, then replace src/main.rs with the whole of this. The project name doesn’t matter, because nothing in the output depends on it:

[1, 2, 3] [1, 2, 3]
[1, 2, 3, 4]

read1 and read2 are alive at the same moment — both are read by the first println! — and the compiler allows it, because shared borrows don’t conflict with each other. write starts only after the last use of the readers, and a single exclusive borrow is fine on its own. The push lands on the original Vec, and the final println! prints what write left behind.

That is the whole of rule one. Many readers or one writer, and “many” and “one” are measured at a moment in time, not per value forever: a &mut that begins after the readers have finished is as legal as the readers were. How long a borrow counts as lasting is its own page later in this chapter.

Overlapping the two kinds is a compile error, and both variants have their real messages captured on the & versus &mut page: E0502 when a mutable borrow starts while a shared one is still alive, and E0499 when two mutable borrows overlap. Neither is a rule about &mut being rare — it’s a rule about overlap.

The second rule is about time rather than exclusivity. A reference has to point at a value that is still alive wherever the reference is used:

error[E0597]: `s` does not live long enough
--> src/main.rs:5:13
|
4 | let s = String::from("hello");
| - binding `s` declared here
5 | r = &s;
| ^^ borrowed value does not live long enough
6 | }
| - `s` dropped here while still borrowed
7 |
8 | println!("{r}");
| - borrow later used here
For more information about this error, try `rustc --explain E0597`.

r is read by the println!, so the borrow has to be alive on that line — but s is dropped when its block ends, one line earlier. The compiler refuses rather than leave a reference pointing at freed memory. Move the owner out to meet the borrow and the same file compiles:

hello

The other fix is to shorten the borrow: move the println! inside the block instead. Either way the owner now covers every line that reads the reference, which is all rule two asks.