# 9.3 The borrow checker is your friend
> The four borrow checker errors every new Rust programmer hits in week one: E0596, E0502, E0382, and E0506, each read line by line and fixed.
Source: https://learnrust.net/chapter-9/the-borrow-checker-is-your-friend/
No new rules today. You have both of them already: one writer or many readers, and references must always be valid. This lesson is a clinic. Four programs walk in with the four complaints every new Rust programmer hears in their first week, and for each one we'll read the compiler's diagnosis line by line, identify the actual disease, and apply the fix. The fixes are worth collecting, because there are only four of those too: add `mut`, end the borrow earlier, borrow instead of move, or reorder.
One reframe before the first patient. New Rust programmers tend to experience this chapter's errors as the compiler being difficult. The C++ versions of all four programs below *compile without a word*, and three of them then read freed memory or worse. The borrow checker is the colleague who points at the bug during code review instead of letting you ship it. It's abrupt, it's always right, and it works for free.
## Patient 1: forgot the `mut` (E0596)
```rust
fn main() {
let s = String::from("hello");
let r = &mut s;
r.push_str(", world");
println!("{r}");
}
```
```
error[E0596]: cannot borrow `s` as mutable, as it is not declared as mutable
--> src/main.rs:3:13
|
3 | let r = &mut s;
| ^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
2 | let mut s = String::from("hello");
| +++
```
Read the headline carefully: "as it is *not declared as mutable*." This isn't a borrow-rules violation at all; it's lesson 1.4 wearing chapter 9 clothing. You can't take a writable loan against a variable the owner declared unchangeable, for the same reason you can't get a key to a room the owner sealed. The help block even writes the fix as a diff: add `mut` on line 2. Every `&mut` needs a `mut` binding behind it, all the way up the chain of consent from last lesson.
## Patient 2: writing while a view is watching (E0502)
Here's lesson [5.4](@/chapter-5/introduction-to-str.md)'s machinery failing honestly for the first time. `trim` hands back a view into the String's own text, and views are borrows:
```rust
fn main() {
let mut name = String::from(" Ada ");
let trimmed = name.trim();
name.push_str("!");
println!("Hello, {trimmed}!");
}
```
```
error[E0502]: cannot borrow `name` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let trimmed = name.trim();
| ---- immutable borrow occurs here
4 | name.push_str("!");
| ^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
5 | println!("Hello, {trimmed}!");
| ------- immutable borrow later used here
```
The three-act structure from last lesson, with a twist: nobody wrote a single `&` in this program. Methods borrow on your behalf. Calling `name.trim()` takes a read-only borrow of `name` (and `trimmed` keeps it alive), while `name.push_str(...)` takes a writable one. The borrow checker sees through the method syntax to the loans underneath.
And this refusal is the one from last lesson's horror story. If `push_str` had run, it could have forced the String to relocate its text to a bigger heap block, leaving `trimmed` pointing into freed memory, which line 5 would then have printed. In C++, the equivalent program compiles, runs, and prints garbage on Tuesdays. The fix is the reordering trick: `trimmed`'s last use is the `println!`, so move that up.
```rust
fn main() {
let mut name = String::from(" Ada ");
let trimmed = name.trim();
println!("Hello, {trimmed}!");
name.push_str("!");
}
```
Reader finishes, then the writer goes. Compiles, and prints `Hello, Ada!`.
## Patient 3: moved when it meant to lend (E0382)
You met E0382 in lesson [8.4](@/chapter-8/moves.md) as the use-after-move error. In week-one practice it almost always arrives in this specific costume: a helper function that takes ownership it never needed.
```rust
fn shout(text: String) -> String {
text.to_uppercase()
}
fn main() {
let cheer = String::from("goal");
let loud = shout(cheer);
println!("{cheer} became {loud}");
}
```
```
error[E0382]: borrow of moved value: `cheer`
--> src/main.rs:8:16
|
6 | let cheer = String::from("goal");
| ----- move occurs because `cheer` has type `String`, which does not implement the `Copy` trait
7 | let loud = shout(cheer);
| ----- value moved here
8 | println!("{cheer} became {loud}");
| ^^^^^ value borrowed here after move
|
note: consider changing this parameter type in function `shout` to borrow instead if owning the value isn't necessary
--> src/main.rs:1:16
|
1 | fn shout(text: String) -> String {
| ----- ^^^^^^ this parameter takes ownership of the value
| |
| in this function
help: consider cloning the value if the performance cost is acceptable
|
7 | let loud = shout(cheer.clone());
| ++++++++
```
This transcript offers two ways out, and choosing between them is the lesson. The `help` block suggests cloning, lesson [8.7](@/chapter-8/clone.md)'s tool, and 8.7 warned you about exactly this moment: cloning here works, but it duplicates the text just to throw the copy away. The `note` block has the real diagnosis, and notice it points at the *function*, not the call: "consider changing this parameter type ... to borrow instead if owning the value isn't necessary." It isn't. `shout` only reads `text`, so it should ask for a view:
```rust
fn shout(text: &str) -> String {
text.to_uppercase()
}
fn main() {
let cheer = String::from("goal");
let loud = shout(&cheer);
println!("{cheer} became {loud}");
}
```
```
goal became GOAL
```
The parameter follows lesson 5.4's best practice (`&str`, not `&String`; the last piece of why falls in lesson [9.7](@/chapter-9/string-slices.md)), the call site lends instead of gives, and `cheer` is alive for line 8. The clones you were told to let wait a chapter? This is what they were waiting for.
## Patient 4: replacing a value someone's watching (E0506)
```rust
fn main() {
let mut label = String::from("Score: 10");
let view = &label;
label = String::from("Score: 20");
println!("{view}");
}
```
```
error[E0506]: cannot assign to `label` because it is borrowed
--> src/main.rs:4:5
|
3 | let view = &label;
| ------ `label` is borrowed here
4 | label = String::from("Score: 20");
| ^^^^^ `label` is assigned to here but it was already borrowed
5 | println!("{view}");
| ---- borrow later used here
```
Why should assignment bother a borrow? Lesson 8.4's fine print: assigning into an existing variable *drops the old value* first. Line 4 would bulldoze the very String that `view` is watching, and line 5 would read the rubble. Same disease as patient 2, different surgery site: there the old text was endangered by growth, here by demolition. Same fix, too. Print first, assign after, and the borrow checker waves it through.
{% callout(kind="tip", title="Tip") %}
When a borrow error has you stuck, find the *last use* of the complaining reference and ask: can this line move up, before the write? Can the write move down? Nine times out of ten the program was correct in spirit and just had its lines in an order the loans can't take turns in.
{% end %}
## Quiz time
**Question #1**
From memory (no compiler): match each error code (E0382, E0502, E0506, E0596) to its one-line description.
a) cannot borrow as mutable because it is also borrowed as immutable
b) borrow of moved value
c) cannot borrow as mutable, as it is not declared as mutable
d) cannot assign because it is borrowed
Show solution
a) E0502. b) E0382. c) E0596. d) E0506. Worth the memorizing: you'll be greeting these four by name for your whole Rust career, and recognizing the code before reading the message is a real speed boost.
**Question #2**
Diagnose (name the error code) and fix this program *without removing or changing the meaning of any line*. It should print the original and the trimmed length.
```rust
fn main() {
let mut input = String::from(" 42 \n");
let cleaned = input.trim();
input.push_str("(processed)");
println!("cleaned: {cleaned}");
println!("raw: {input}");
}
```
Show solution
E0502: `cleaned` is a read-only view into `input` (patient 2's disease), still alive at line 5, while `push_str` wants a writable loan at line 4. Reorder so the reader finishes first:
```rust
fn main() {
let mut input = String::from(" 42 \n");
let cleaned = input.trim();
println!("cleaned: {cleaned}");
input.push_str("(processed)");
println!("raw: {input}");
}
```
```
cleaned: 42
raw: 42
(processed)
```
(The raw line really does print the leftover whitespace and newline before `(processed)`; that's what "raw" means.)
**Question #3**
Diagnose and fix, choosing the *cheapest* correct fix:
```rust
fn banner(title: String) -> String {
title.to_uppercase()
}
fn main() {
let name = String::from("results");
let top = banner(name);
println!("== {top} ==");
println!("(section: {name})");
}
```
Show solution
E0382, patient 3's costume: `banner` takes ownership it doesn't need, so `name` is dead by the last line. The cheap fix is borrowing, not cloning: change the signature to `fn banner(title: &str) -> String` and the call to `banner(&name)`. Now nothing is copied, nothing moves, and both `println!` lines work. `banner(name.clone())` also compiles, but it pays for a full copy of the text to fix what one `&` fixes for free.
Four diseases, four fixes, zero new rules. What's still missing is judgment: when *should* a function take ownership, a view, or a writable loan? That's not an error message question, it's a design question, and it gets the next lesson to itself.