---
title: "9.2 Mutable references"
description: "The &mut reference lets a function modify a borrowed value, under Rust's one-writer-or-many-readers rule, enforced by the borrow checker."
url: "https://learnrust.net/chapter-9/mutable-references/"
last_updated: "2026-06-12"
---

# 9.2 Mutable references

In lesson [1.6](https://learnrust.net/chapter-1/reading-input/) you wrote this line on faith, and a callout box promised that "lesson 9.2 pays this off exactly":

```rust
io::stdin().read_line(&mut name).expect("failed to read line");
```

This is that lesson. After eight chapters, every word of the input recipe is finally yours: `read_line` appends what the user typed to `name` (lesson 1.6), `.expect` is the blunt error tool (lesson 3.x's crash-site message, properly retired in chapter 12), and `&mut name`... lends `read_line` a reference through which it is *allowed to write*. The function needs to put the user's text somewhere that's yours, so a read-only window won't do. It needs a **mutable reference**.

## &mut on both sides

Last lesson's `add_world` was refused for writing through a plain `&`. Here it is with permission granted:

```rust
fn add_world(text: &mut String) {
    text.push_str(", world");
}

fn main() {
    let mut message = String::from("hello");
    add_world(&mut message);
    println!("{message}");
}
```

```
hello, world
```

Count the places mutation is written down, because there are three and all of them are mandatory. The binding is `let mut message` (without it, nothing may change the variable at all, lesson 1.4's rule). The call site says `&mut message`: the owner consents, in writing, to this particular loan being a writable one. And the signature says `text: &mut String`: the function declares what it intends to do. Owner consents, function declares, compiler checks that all three stories match.

> **Author's note**
>
> learncpp's chapter on this (12.13) spends paragraphs on a genuine C++ pain: at a C++ call site, `getSinCos(degrees, sin, cos)` gives no hint that `sin` and `cos` are about to be overwritten. You have to go read the function's declaration to find out. I get to teach the language where that complaint is impossible: in Rust, a call that can modify your variable says `&mut` right there at the call site, or it doesn't compile. The annoying extra typing is the documentation.

So `read_line(&mut name)` now parses completely: "here is a writable loan of `name`; fill it." That's also why lesson 1.7's most famous beginner error happens. Forget the `&mut` and you've offered to *give* `read_line` the String (a move, chapter 8) when it asked for a writable loan; the types disagree and the compiler hands you the fix.

## One writer at a time

Plain references could be handed out freely, any number at once. Mutable references come with the restriction that gives rule 1 its teeth: while a mutable reference to a value exists, *no other reference to that value may exist at all*. One writer, or many readers, never both, and never two writers. Try to take two writable loans and the borrow checker steps in:

```rust
fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{r1}, {r2}");
}
```

```
error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> src/main.rs:5:14
  |
4 |     let r1 = &mut s;
  |              ------ first mutable borrow occurs here
5 |     let r2 = &mut s;
  |              ^^^^^^ second mutable borrow occurs here
6 |
7 |     println!("{r1}, {r2}");
  |                -- first borrow later used here
```

This error's shape is worth memorizing, because the borrow checker always tells its stories in three labeled acts: where the first borrow happened, where the conflicting borrow happened, and where the first borrow is *still being used* later. All three lines matter. If that third line didn't exist, neither would the error, which is the escape hatch we'll get to in a moment.

Mixing flavors is refused just as firmly. Readers are promised the value won't change under them, so a writer can't move in while they're watching:

```rust
fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // problem!

    println!("{r1}, {r2}, and {r3}");
}
```

```
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
 --> src/main.rs:6:14
  |
4 |     let r1 = &s; // no problem
  |              -- immutable borrow occurs here
5 |     let r2 = &s; // no problem
6 |     let r3 = &mut s; // problem!
  |              ^^^^^^ mutable borrow occurs here
7 |
8 |     println!("{r1}, {r2}, and {r3}");
  |                -- immutable borrow later used here
```

## Why so strict?

Because the alternative is the bug class that has cost the software industry the most sleep. If one part of a program can change a value while another part is reading it, the reader sees half-finished states. Run those two parts on different processor cores at the same time (threads, chapter 22) and you have a **data race**, a bug that corrupts data based on which core happens to win a footrace, unreproducible by design. Rust's headline feature is that its compiler makes data races impossible, and this rule is how: code that *could* race doesn't compile.

But you don't need threads to get burned, and chapter 5's bicycle is the cleanest way to see it. A `&str` view into a `String` is a window onto the owner's heap text (lesson [5.4](https://learnrust.net/chapter-5/introduction-to-str/)). Now let a writer push more text in while that view exists: growing a `String` can force it to relocate to a bigger heap block (the move-to-a-bigger-block story lesson [8.2](https://learnrust.net/chapter-8/the-stack-and-the-heap/) told about `push_str`), and the view is left pointing at the old, freed address. The neighbor stares at an empty driveway, except this time the neighbor is your program, reading memory that's been handed back. C++ allows exactly this, and lesson 9.7 will show you Rust refusing it with a transcript. One-writer-or-many-readers isn't strictness for its own sake; it's the precise condition under which a view can never lie to you.

## Borrows end at their last use

The good news, and the fact that dissolves most fights with the borrow checker: a borrow doesn't last until the closing brace. It lasts until the *last place it's used*. Watch both rules relax in a perfectly legal program:

```rust
fn main() {
    let mut s = String::from("hello");

    let r1 = &s;
    let r2 = &s;
    println!("{r1} and {r2}");

    let r3 = &mut s;
    r3.push_str(", world");
    println!("{r3}");
}
```

```
hello and hello
hello, world
```

`r1` and `r2` take their last bow in the first `println!`, so by the time `r3` is created the readers are gone and the writer is welcome. Sequential loans are always fine; it's *overlapping* ones the rules constrain. When the borrow checker refuses your program, the first thing to check is whether reordering a line or two makes the borrows take turns instead of overlap. It usually does.

> **Key insight**
>
> A reference's life runs from where it's created to where it's last used, not to the end of the block. The compiler tracks this per reference, which is why "move the `println!` up a line" is a real and honorable fix for a borrow error.

## Quiz time

**Question #1**

For each program, say whether it compiles. If it does, give the output; if not, name the rule it breaks.

a)

```rust
fn main() {
    let mut word = String::from("hi");
    let r = &word;
    word.push_str("!");
    println!("{r}");
}
```

b)

```rust
fn main() {
    let mut word = String::from("hi");
    let r = &word;
    println!("{r}");
    word.push_str("!");
    println!("{word}");
}
```

c)

```rust
fn main() {
    let mut word = String::from("hi");
    word.push_str("!");
    let r = &word;
    println!("{r}");
}
```

<details class="solution">
<summary>Show solution</summary>

a) Refused, E0502. `push_str` needs a writable borrow of `word` (it's a method that modifies, so it borrows `&mut` behind the scenes), but the reader `r` is still alive: it gets used in the `println!` afterward. One writer or many readers, never both at once.

b) Compiles, printing `hi` then `hi!`. Same four lines as (a), different order: `r`'s last use is the first `println!`, so the borrow has ended before `push_str` needs the value. Taking turns is always legal.

c) Compiles, printing `hi!`. The write happens first, the reader is created after. No overlap, no complaint.

</details>

**Question #2**

Why does `read_line` need `&mut name` rather than `&name`? Answer in ownership-and-borrowing words.

<details class="solution">
<summary>Show solution</summary>

`read_line`'s whole job is to *modify* the String you lend it: it appends the user's typed line to it. A `&name` loan is read-only, and the compiler holds functions to that promise, so `read_line` couldn't do its job through one. It asks for `&mut String`: a writable loan. It borrows rather than takes ownership because the buffer is yours: you'll want to `trim` and use it after the call, and lesson [8.6](https://learnrust.net/chapter-8/ownership-and-functions/)'s give-it-back dance would be a terrible API for something called ten times per program.

</details>

**Question #3**

Without changing any line's text, reorder this program's `main` so it compiles, and give its output:

```rust
fn shout(text: &mut String) {
    text.push_str("!!!");
}

fn main() {
    let mut cheer = String::from("goal");
    let watcher = &cheer;
    shout(&mut cheer);
    println!("{watcher}");
    println!("{cheer}");
}
```

<details class="solution">
<summary>Show solution</summary>

Move the watcher's lines before the shout (reader takes its turn, then the writer):

```rust
fn main() {
    let mut cheer = String::from("goal");
    let watcher = &cheer;
    println!("{watcher}");
    shout(&mut cheer);
    println!("{cheer}");
}
```

```
goal
goal!!!
```

The original overlapped a live reader with a writable loan (E0502: `watcher` was created before the `shout` call and used after it). Reordered, `watcher`'s last use comes first, its borrow ends, and `shout` gets the value to itself.

</details>

You now know both kinds of loans and the rule that governs them. Next lesson is pure practice: the four borrow-checker errors every Rust programmer meets in week one, each read line by line and fixed, so that when they happen to you they'll feel like old acquaintances.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
