Lesson 5.6 · 8 min
Chapter review: Shapes
Chatterbox has real types now: a Message with methods and a Command for every kind of line. Before chapter 6 gives it lists and lookups, check that the five ideas behind those types are solid: a mixed quiz, then one bigger exercise that needs all of them at once.
Adds to ChatterboxNothing new: a mixed quiz on the whole chapter, and a bigger exercise that uses all of it.
What chapter 5 gave Chatterbox
At the end of chapter 4, Chatterbox was a loop of loose strings. Now it has shapes of its own: every line is a Message, every command is a Command, and "nothing there" is a value the compiler makes you handle. Five ideas did that:
- 5.1 struct: a named bundle of fields that travel together and have one owner.
- 5.2 methods: an
implblock puts actions next to the data;&selflooks,&mut selfchanges, plainselftakes the value. - 5.3 enum: a value that is exactly one of several variants, each able to carry data, and a
matchmust cover them all. - 5.4 Option:
Some(value)orNone, so a missing value cannot be used by accident. - 5.5 patterns: arms that use
|, ranges and guards, look inside values, and take them apart.
Nothing new comes in this lesson. Every answer below was produced by the real compiler, so a wrong guess is worth more than a right one: it tells you exactly which lesson to reopen.
Mixed quiz
The questions jump between ideas on purpose. Real code never tells you which lesson it comes from.
What does this print?
hello! (edited 2 times)helo (edited 0 times)hello (edited 1 times)
Reveal the answer
The program prints: hello! (edited 2 times) edit takes &mut self, so it changes the message where it lives: each call replaces the text and adds one to edits. label only looks with &self and sees the result of both edits. If the change seemed to vanish, reopen lesson 5.2.
Does this compile?
- It compiles and runs
- Compile error E0382
- Compile error E0596
Reveal the answer
It does not compile: E0382. A struct owns its fields, and a single field can be handed over on its own: archive(said.text) moves the text out. Printing said.author afterwards would be fine, but said.text is gone, so the compiler refuses with the moved-value error. Lesson 5.1 shows a field moving out; lesson 4.3 is the rule behind it.
error[E0382]: borrow of moved value: `said.text` --> src/main.rs:16:37 | 15 | archive(said.text); | --------- value moved here 16 | println!("{}: {}", said.author, said.text); | ^^^^^^^^^ value borrowed here after move | = note: move occurs because `said.text` has type `String`, which does not implement the `Copy` trait
What does this print?
talking to myself? / hi, whoever you arehi bot / hi, whoever you aretalking to myself? / hi
Reveal the answer
The program prints: talking to myself? / hi, whoever you are A pattern can look inside an Option: Some("bot") fits only a Some holding exactly that text, and arms are tried from the top, so it wins before Some(n) gets a turn. None has its own arm with its own answer. Lessons 5.4 and 5.5 cover both halves.
What does this print?
[HI][][hi][][HI][Silent]
Reveal the answer
The program prints: [HI][] Each variant carries its own data, or none: Shout carries "hi", which its arm names t and capitalises, and Silent carries nothing and gives back an empty String. A variant's name is never printed unless you print it. Reopen lesson 5.3 if the brackets surprised you.
Does this compile?
- It compiles and runs
- It compiles, then panics
- Compile error E0004
Reveal the answer
It does not compile: E0004. The program never calls it with 2, but the compiler does not care what you happen to pass: a match must cover every value its input could have. Some(2) and every bigger number have no arm, so it refuses with E0004 and names the missing range. An arm like Some(n) => … fixes it; see lesson 5.5.
error[E0004]: non-exhaustive patterns: `Some(2_u32..=u32::MAX)` not covered --> src/main.rs:2:11 | 2 | match unread { | ^^^^^^ pattern `Some(2_u32..=u32::MAX)` not covered | note: `Option<u32>` defined here --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/option.rs:598:0 ::: /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/option.rs:606:4 | = note: not covered = note: the matched value is of type `Option<u32>` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 5 ~ Some(1) => String::from("1 new message"), 6 ~ Some(2_u32..=u32::MAX) => todo!(), |
Does this compile?
- It compiles and runs
- Compile error E0596
- Compile error E0382
Reveal the answer
It does not compile: E0596. show only borrowed the message to look at it (&Message), but shout asks for &mut self, a borrow that may change it. You cannot get a changing borrow out of a looking one, even though main passed &mut m: the signature of show is what counts. Lesson 5.2 explains what each kind of self promises.
error[E0596]: cannot borrow `*m` as mutable, as it is behind a `&` reference --> src/main.rs:12:5 | 12 | m.shout(); | ^ `m` is a `&` reference, so it cannot be borrowed as mutable | help: consider changing this to be a mutable reference | 11 | fn show(m: &mut Message) { | +++
What does this print?
quiet robotrobot robotquiet liked
Reveal the answer
The program prints: quiet robot Arms are tried from the top. a has 0 likes, so the first arm fits before its author is ever checked. b skips the first arm, and the second takes its author apart with .. skipping the rest; the guard asks whether it is "bot", it is, so "liked" never gets a turn. Lesson 5.5 has guards and ...
What does this print?
Say(None)sayHelp
Reveal the answer
The program prints: Say(None) "/say" has no space, so find gives None, and the guard picks Command::Say(None). The if let wants a Say with text inside, which this is not, so the else prints the debug form that #[derive(Debug)] wrote for you. Lessons 5.4 and 5.5.
Milestone: a bot with a name
Time to put all five ideas in one place. This is a small, separate program, but it is shaped exactly like Chatterbox: a Command enum, a parse that turns a line into one, and a Bot struct whose answer method replies. It adds one thing Chatterbox does not have yet, /rename, which changes the bot's own name, so the struct has to change itself.
The doc comments above each function (the /// lines) say exactly what it must do. first_word and arg are already written: they are the helpers from lessons 4.6 and 5.4.
Fill in the three todo!() bodies: parse, Bot::new and Bot::answer. You will need a struct, a method that changes it, an enum with data, Option, and patterns with | and a guard. The hidden tests check every command, the short forms, a bare /rename, the reply counter and the empty line.
Fill in the three todo!() bodies: parse, Bot::new and Bot::answer. You will need a struct, a method that changes it, an enum with data, Option, and patterns with | and a guard. The hidden tests check every command, the short forms, a bare /rename, the reply counter and the empty line.
Hidden tests (11): a_bare_rename_carries_nothing a_new_bot a_bare_rename_keeps_the_name an_empty_line_is_not_counted chats_are_counted_and_signed help_and_quit_answers commands_and_their_short_forms rename_carries_the_name rename_changes_who_answers unknown_commands_and_chat unknown_names_the_word
This exercise runs real Rust in your browser through the official Rust Playground, so it needs JavaScript.
Where to go from here
If a quiz answer surprised you, reopen the lesson its explanation names before you go on. Chapter 6 keeps every one of these types and starts collecting them: a Vec of messages, then a lookup table of facts. It leans on Option constantly, because asking a list for an item that is not there gives you a None.
Say it out loud, then reveal
When do you reach for a struct, and when for an enum?Reveal
What do <code>&self</code>, <code>&mut self</code> and <code>self</code> each promise the caller?Reveal
&self only looks. &mut self may change the value, so it needs a value that may change. Plain self takes the value, so the caller cannot use it afterwards.Why can you not use an <code>Option<&str></code> as if it were a <code>&str</code>?Reveal
None. You have to say what happens in that case first, with match, if let or unwrap_or.An arm has a guard and the guard says no. What happens?Reveal
Your Chatterbox so far: v0.5 (after lesson 5.5)
Download this version (zip)Unzip, then cargo run inside the folder.
That is lesson 5.6. Tick it off so the course remembers where you are. That is also the end of chapter 5: 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 should I know before starting Rust collections?
How to define a struct and give it methods, how an enum with data works, how to handle Option with match, if let or unwrap_or, and how match arms use patterns. Chapter 6 builds lists and lookup tables out of exactly those pieces.
What is the difference between a struct and an enum in Rust?
A struct holds all of its fields at the same time. An enum holds exactly one of its variants at a time, and each variant can carry its own data.
Why does Rust complain about a match that my program never reaches?
The compiler checks every value the matched type could have, not just the ones your program happens to pass. That is what lets it point at a forgotten case before the program ever runs.
Can a method change the struct it belongs to?
Yes, if it takes self by mutable reference, written ampersand mut self. The value it is called on must then be declared with let mut, and the caller must hold a mutable borrow of it.
