Vibe Engines
YouTube

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.

record borrows the log with edit rights and adds a line
fn record(log: &mut String, msg: &str) {
log.push_str(msg);
log.push('\n');
}
 
fn main() {
let mut log = String::new();
record(&mut log, "you: hi");
record(&mut log, "bot: hello");
print!("{log}");
}
$ cargo run
you: hi
bot: hello

Read 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.

Ownership Stagerecord(&mut log, ...) borrows with edit rights

Step through the program. Solid arrows are ownership; dashed arrows are borrows. Use ← → or the slider.

fn record(log: &mut String, msg: &str) {
log.push_str(msg);
log.push('\n');
}
 
fn main() {
let mut log = String::new();
record(&mut log, "you: hi");
record(&mut log, "bot: hello");
print!("{log}");
}
  1. Line 6. The program starts in main.
  2. Line 7. An empty String is made. log owns it, and it is declared mut, which means its owner allows it to change.
  3. 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 String borrows log.
  4. Line 2. push_str adds the text. The change happens to the owner's String, since the borrower points straight at it.
  5. Line 4. The function ends and the loan is over. Nothing is cleaned up, because the borrower never owned the String.
  6. 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.

PredictTwo editors of the same transcript
fn main() {
let mut log = String::new();
let a = &mut log;
let b = &mut log;
a.push_str("you: hi\n");
b.push_str("bot: hello\n");
print!("{log}");
}

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
Borrow TimelineHow long does each borrow live?

Each bar is one borrow, drawn from its first use to its last. Scrub the line to see what is alive.

fn main() {
let mut log = String::new();
let a = &mut log;
let b = &mut log;
a.push_str("you: hi\n");
b.push_str("bot: hello\n");
print!("{log}");
}
  • log: owner — owner, lines 1–7
  • log: a = &mut log — mutable borrow, lines 3–5
  • log: b = &mut log — mutable borrow, lines 4–6
Compiler CoachThe compiler, translated

This is the exact output of the real compiler. Toggle the English, then pick a fix and see what actually happens.

fn main() {
let mut log = String::new();
let a = &mut log;
let b = &mut log;
a.push_str("you: hi\n");
b.push_str("bot: hello\n");
print!("{log}");
}
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 →

  1. 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.
  2. first mutable borrow occurs here points at line 3, where a was made.
  3. second mutable borrow occurs here points at line 4, where b tried to start.
  4. first borrow later used here points 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>&amp;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.

src/main.rs: a running transcript
fn reply(msg: &str) -> String {
match msg {
"/help" => String::from("commands: /help, /quit"),
"/quit" => String::from("bye"),
_ => format!("echo: {msg}"),
}
}
 
fn shout(msg: &str) -> String {
msg.to_uppercase()
}
 
fn record(log: &mut String, who: &str, msg: &str) {
log.push_str(who);
log.push_str(": ");
log.push_str(msg);
log.push('\n');
}
 
fn main() {
let mut transcript = String::new();
let msg = String::from("/help");
let answer = reply(&msg);
record(&mut transcript, "you", &msg);
record(&mut transcript, "bot", &answer);
print!("{transcript}");
}
$ cargo run
you: /help
bot: commands: /help, /quit
PredictHow long is the transcript?
fn add(log: &mut String, msg: &str) {
log.push_str(msg);
log.push('\n');
}
 
fn main() {
let mut log = String::new();
add(&mut log, "hi");
add(&mut log, "yo");
println!("{}", log.len());
}

What does this print?

  • 6
  • 4
  • 2
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.

Try itYour turn: write the transcript line

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.

fn record(log: &mut String, who: &str, msg: &str) {
// append: who, ": ", msg, then a newline
}
 
fn main() {
let mut log = String::new();
record(&mut log, "you", "hi");
record(&mut log, "bot", "hello");
print!("{log}");
}

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.

Self-check

Say it out loud, then reveal

Self-checkRetrieval beats re-reading
What does &mut let a borrower do that & does not?Reveal
Change the value. It still does not own it, so nothing is cleaned up when the borrower finishes.
What is the rule about how many borrows can exist at once?Reveal
Any number of & readers, or exactly one &mut editor. Never a reader and an editor, and never two editors.
Which error do you get for two &mut at once?Reveal
E0499, cannot borrow as mutable more than once at a time.

That is lesson 4.4. Tick it off so the course remembers where you are.

Next: 4.5 Parts of a value →
Learn Rust by building a chat app · Lesson 4.4 · every compiler message above is real output from rustc 1.99.0 (b940084d7 2026-09-28) (edition 2024) · Vibe Engines · 2026