Lesson 4.7 · 9 min
Chapter review: Memory
Chapter 4 was the one Rust is famous for: where values live, who holds the one key, and the two ways to lend it. Here it all is in one sitting: a mixed quiz where every answer came from the real compiler, and one exercise that pulls a command apart, keeps a transcript and hands back answers it owns.
Adds to ChatterboxNothing new: a mixed quiz on the whole chapter, and a bigger exercise that uses all of it.
What chapter 4 gave Chatterbox
Chatterbox 0.4 remembers your last message for /again, keeps a transcript for /history, answers /say with whatever follows it, and tells you when a command is unknown. Under those four features sit the ideas of the chapter:
- Lesson 4.1, where values live: a value whose size is known while compiling sits in a frame on the stack; text that can grow lives in a heap block, and a String's 24 bytes in the frame are a pointer, a length and a capacity.
- Lesson 4.2 and lesson 4.3, one owner, and handing over the key: every value has exactly one owner and is cleaned up when that owner goes away. Passing a String by value or assigning it moves the key, and the old name is done (E0382). Small Copy values such as numbers are duplicated instead, and
.clone()makes a real second room when you ask for it. - Lesson 4.4, borrowing:
&lends a value to look at; the owner keeps the key. While a loan is still going to be used, the owner may not change the value (E0502). - Lesson 4.5, borrowing to change:
&mutlends with edit rights, to exactly one editor at a time (E0499), and only from a value made withmut. - Lesson 4.6, slices:
&line[a..b]is a borrowed window onto part of the text. It copies nothing, counts bytes, and keeps the loan alive like any other borrow.
Nothing new below. The questions jump between those ideas on purpose, because the first job on each one is working out which rule it is about. Commit to an answer before you open it.
Mixed quiz
Eight short programs. Some ask whether the compiler accepts the program, and if not, which error it gives; some ask what it prints. Every answer was produced by running the real Rust compiler on exactly this code.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0382
- Compile error E0499
Reveal the answer
It does not compile: E0382. The first turn of the loop moves the key from said to last. The second turn tries to move it again, but said owns nothing any more, so the compiler reports E0382 and says the value was moved "in previous iteration of loop". A plain = moves a String exactly like passing it to a function. Revisit lesson 4.3.
error[E0382]: use of moved value: `said` --> src/main.rs:5:16 | 2 | let said = String::from("hello"); | ---- move occurs because `said` has type `String`, which does not implement the `Copy` trait 3 | let mut last = String::new(); 4 | for _ in [1, 2] { | --------------- inside of this loop 5 | last = said; | ^^^^ value moved here, in previous iteration of loop | help: consider cloning the value if the performance cost is acceptable | 5 | last = said.clone(); | ++++++++
What does this print?
10 1610 2415 1616 10
Reveal the answer
The program prints: 10 16 The slice covers the first 10 bytes, Chatterbox, so its len() is 10. Every &str is 16 bytes in the frame, a pointer and a length, however much text it looks at; 24 would be a String, which also carries a capacity. Revisit lesson 4.1 for the sizes and lesson 4.6 for the slice.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0502
- Compile error E0382
Reveal the answer
It does not compile: E0502. A slice is a borrow, and first is used on the last line, so its loan is still out when push_str wants to change history. Growing the String might move its text to a bigger block and leave the slice pointing at the old one, so the compiler reports E0502. Print first before the push and it compiles. Revisit lesson 4.4 and lesson 4.6.
error[E0502]: cannot borrow `history` as mutable because it is also borrowed as immutable --> src/main.rs:4:5 | 3 | let first = &history[..7]; | ------- immutable borrow occurs here 4 | history.push_str("you: again\n"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here 5 | println!("{first}"); | ----- immutable borrow later used here
What does this print?
4 55 54 4
Reveal the answer
The program prints: 4 5 A u32 is Copy: its 4 bytes are the whole value, so bump receives its own duplicate. Changing that duplicate leaves messages_seen at 4, and the returned 5 lands in next. Nothing moved, so both names still work. Revisit lesson 4.3, "Why numbers do not move".
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0596
- Compile error E0384
Reveal the answer
It does not compile: E0596. push_str needs an editing loan of the String, and an editing loan can only be made from a value its owner allows to change. history was made without mut, so the compiler reports E0596, cannot borrow as mutable. E0384 would be assigning a new value with =; this is a change through a method. Revisit lesson 4.5.
error[E0596]: cannot borrow `history` as mutable, as it is not declared as mutable --> src/main.rs:3:5 | 3 | history.push_str("you: hi\n"); | ^^^^^^^ cannot borrow as mutable | help: consider changing this to be mutable | 2 | let mut history = String::new(); | +++
What does this print?
hi hi!hi! hi!hi hi
Reveal the answer
The program prints: hi hi! .clone() makes a second String with its own heap block and its own key, so last and said share nothing. Adding to last changes only its block. That independence is exactly what a clone costs you a copy for. Revisit lesson 4.3.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0502
- Compile error E0499
Reveal the answer
It compiles and runs. A borrow lasts from where it is made to its last use, not to the end of the block. cmd is last used on line 4, so by the time clear changes line the loan is over, and the program prints /say and then []. Compare it with the history question above: same shape, different order. Revisit lesson 4.4.
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. Both arguments are editing loans of the same transcript, and both are alive for the whole call. Two &mut at once is E0499: inside the function, clearing one would empty the other while it was being copied. E0502 is for an editor meeting a reader. Revisit lesson 4.5.
error[E0499]: cannot borrow `transcript` as mutable more than once at a time --> src/main.rs:8:32 | 8 | copy_into(&mut transcript, &mut transcript); | --------- --------------- ^^^^^^^^^^^^^^^ second mutable borrow occurs here | | | | | first mutable borrow occurs here | first borrow later used by call
The milestone: answer a line, keep the transcript
Now use the whole chapter at once. Three functions, framed exactly as Chatterbox needs them:
split_commandcuts a line at its first space and gives back both parts as a pair, a tuple from lesson 2.6. Both parts are slices of the line: nothing is copied. With no space, the second part is"".recordborrows the transcript with edit rights and adds one line, such asyou: helloand a newline.answerborrows the line, borrows the transcript with edit rights, and gives back a String the caller owns./say hi thereanswershi there, and a bare/sayanswerssay what?./historyanswers a copy of the whole transcript, ornothing said yet. Any other word starting with/answersunknown command:and that word. Anything else is a message: record it asyou, record the reply asbot, and answeryou said:and the line. Commands are never recorded.
Two places need the chapter's judgement rather than its syntax. /history must hand the caller a String of its own while the transcript stays where it is, so that is the one honest place for a clone. And for a message, the reply is used by record and then handed to the caller: use it first, hand it over last.
Write split_command, record and answer. The program in main plays four lines; the hidden tests also try a bare /say, two spaces in a row, an empty history, a slash in the middle of a word, and a line that is a borrowed String.
Write split_command, record and answer. The program in main plays four lines; the hidden tests also try a bare /say, two spaces in a row, an empty history, a slash in the middle of a word, and a line that is a borrowed String.
Hidden tests (9): a_slash_inside_a_word_is_still_a_message a_message_is_answered_and_recorded_twice history_is_a_copy_and_the_transcript_stays no_space_means_an_empty_second_part record_adds_one_line_and_keeps_the_rest say_answers_its_text_and_records_nothing splits_at_the_first_space the_caller_keeps_its_line unknown_commands_are_named_by_their_first_word
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
What you have now
If the tests are green, you just wrote, without a single unnecessary copy, the three moves the chapter is about: windows onto text you do not own, a loan with edit rights that ends with each call, and a new value whose key you hand to the caller. The clone in /history is there because the caller really needs a room of its own, not to quiet the compiler.
Chatterbox now knows five commands, but its messages and commands are still loose pieces of text and loose variables in main. The next chapter gives them shapes of their own: a Message that bundles who said what, and a Command that can only be one of the commands Chatterbox knows.
Say it out loud, then reveal
In one sentence each: what do moving, borrowing with &, and borrowing with &mut do to the key?Reveal
& lends a look while the owner keeps the key. &mut lends edit rights, to one borrower at a time, and the owner still keeps the key.Why is a slice of a String a borrow, and what does that stop you doing?Reveal
Your code fails with E0382. Name a fix that costs nothing and one that costs a copy.Reveal
& instead of passing it by value. Costs a copy: .clone(), which makes a second String with its own heap block.Why is a String 24 bytes whatever its text, while a u32 can be copied freely?Reveal
u32's 4 bytes are the whole value, so a duplicate is complete and nothing has to be handed back twice.Your Chatterbox so far: v0.4 (after lesson 4.6)
Download this version (zip)Unzip, then cargo run inside the folder.
That is lesson 4.7. Tick it off so the course remembers where you are. That is also the end of chapter 4: a few minutes with the course flashcards now makes it stick, and the chapter’s cheat sheet has every idea on one screen.
Go deeper
Questions people ask
What does chapter 4 of the Rust course cover?
Memory and ownership: the stack and the heap, the rule that every value has one owner, moving a value and error E0382, borrowing with & and error E0502, borrowing to change with &mut and error E0499, and slices, which are borrowed parts of a value.
How do I tell E0382, E0502 and E0499 apart?
E0382 means you used a value after its key was handed away. E0502 means you changed a value while a read-only borrow of it was still going to be used. E0499 means two borrows with edit rights of the same value were alive at once.
Is it bad to use clone to fix ownership errors?
Not always, but it copies the whole value. First try using the value before handing it over, or lending it. Clone when the other code genuinely needs its own copy, such as handing someone a snapshot of a transcript that keeps changing.
Why does the borrow checker sometimes accept code that looks like the code it refused?
A borrow lasts until its last use, not until the end of the block. Move the last use of a borrow above the line that changes the value and the two no longer overlap, so the same lines in a different order compile.
