Lesson 4.4 · 11 min
Lending with edit rights
Chatterbox needs a running transcript, and a function has to be able to add to it without owning it. That takes a second kind of loan, &mut, with one strict rule: only one at a time.
Adds to ChatterboxA function that edits the history.
A loan with edit rights
A plain & loan lets the borrower look. Chatterbox now wants a transcript: one growing String holding every line said so far. A function that adds to it must change it, but it should not own it, because main still needs the transcript when the function is done.
That is the second loan: &mut, "borrow with edit rights". It follows the same shape as before, with two small marks. The value must be declared mut, and the call says &mut so a reader sees at the call site that this line may change what is passed.
$ cargo run
you: hi
bot: helloRead the two types in the signature aloud: log: &mut String is "an editable loan of a String" and msg: &str is "a look at some text". The function can push onto log and only read msg. When record returns, the loan is over and main carries on with the longer transcript.
Step through the program. Solid arrows are ownership; dashed arrows are borrows. Use ← → or the slider.
- Line 6. The program starts in main.
- Line 7. An empty String is made. log owns it, and it is declared mut, which means its owner allows it to change.
- Line 8. record(&mut log, ...) lends log with edit rights. The borrower, also called log inside record, may change the text but does not own it.
log_ref:&mut Stringborrowslog. - Line 2. push_str adds the text. The change happens to the owner's String, since the borrower points straight at it.
- Line 4. The function ends and the loan is over. Nothing is cleaned up, because the borrower never owned the String.
- Line 10. main prints the transcript it still owns. It includes the line record added. stdout:
you: hi
Only one editor
Edit rights are exclusive, and the reason is the same as in 4.3. Two editors at once could both be rewriting the same text, and neither would know what the other did. So the rule is: at any moment, either any number of readers or exactly one editor, never both and never two editors. Here is the second half of that sentence being broken.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0499
- Compile error E0502
Reveal the answer
It does not compile: E0499. a is an editable loan of log and it is used again on line 5, after b asks for a second one on line 4. Two &mut at once is E0499. The E0502 code is for an editor meeting a reader.
error[E0499]: cannot borrow `log` as mutable more than once at a time --> src/main.rs:4:13 | 3 | let a = &mut log; | -------- first mutable borrow occurs here 4 | let b = &mut log; | ^^^^^^^^ second mutable borrow occurs here 5 | a.push_str("you: hi\n"); | - first borrow later used here
Each bar is one borrow, drawn from its first use to its last. Scrub the line to see what is alive.
log: owner — owner, lines 1–7log: a = &mut log — mutable borrow, lines 3–5log: b = &mut log — mutable borrow, lines 4–6
This is the exact output of the real compiler. Toggle the English, then pick a fix and see what actually happens.
error[E0499]: cannot borrow `log` as mutable more than once at a time --> src/main.rs:4:13 | 3 | let a = &mut log; | -------- first mutable borrow occurs here 4 | let b = &mut log; | ^^^^^^^^ second mutable borrow occurs here 5 | a.push_str("you: hi\n"); | - first borrow later used here
E0499 in the compiler error atlas →
cannot borrow `log` as mutable more than once at a time: that is the whole rule. You asked for the editing loan twice, and the first one was still being used.first mutable borrow occurs herepoints at line 3, whereawas made.second mutable borrow occurs herepoints at line 4, wherebtried to start.first borrow later used herepoints at line 5, the line that keeps the first editor alive. Without it there would be no overlap, so no error.
Fixes that were compiled for real
- Let the first editor finish — compiles. Each editor now starts after the previous one has made its last use. They never overlap, so there is nothing to refuse.
- Skip the names and call a function — compiles. A function that takes &mut gets its loan for one call and gives it back at once. This is the usual shape in real code.
- Trap: forgetting mut on the owner — still E0596. A loan with edit rights can only be made from something that allows edits. Without <code>mut</code> on the variable, the compiler stops you at <code>&mut log</code>.
Chatterbox v0.4
Chatterbox now keeps a transcript. record adds one line per speaker, and main prints the whole thing at the end. The new lines are marked.
$ cargo run
you: /help
bot: commands: /help, /quitWhat does this print?
642
Reveal the answer
The program prints: 6 Each call adds the text plus a newline: hi and \n make 3 bytes, yo and \n make 3 more. Both calls changed the same String because each borrowed it with edit rights in turn.
Make record add who: msg and a newline to the end of the borrowed log. Do not return anything; the change is made on the owner's String.
Make record add who: msg and a newline to the end of the borrowed log. Do not return anything; the change is made on the owner's String.
Hidden tests (3): keeps_what_was_there lines_stack_up one_line
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
Say it out loud, then reveal
What does &mut let a borrower do that & does not?Reveal
What is the rule about how many borrows can exist at once?Reveal
Which error do you get for two &mut at once?Reveal
That is lesson 4.4. Tick it off so the course remembers where you are.