Lesson 3.5 · 8 min
Chapter review: Decisions
Chapter 3 taught Chatterbox to react: to choose an answer, to recognise a command, to keep going and to stop, and to read what you type. Here is the whole chapter in one sitting: a mixed quiz where every answer came from the real compiler, and one exercise that plays a complete chat.
Adds to ChatterboxNothing new: a mixed quiz on the whole chapter, and a bigger exercise that uses all of it.
What chapter 3 gave Chatterbox
At the start of the chapter Chatterbox printed the same lines every time it ran. Now it is version 0.3: it asks your name, answers each line you type, knows /help and /quit, and stops when you leave or the input ends. Four ideas did that:
- Lesson 3.1, if: run one block or another, depending on a question that is really
trueorfalse. Anifalso gives back a value, which is why the whole body ofreplycan be oneelse ifchain. - Lesson 3.2, match: compare one value against a list of arms, top to bottom, and run the first that fits. Every possible value must have an arm, so text needs a catch-all.
- Lesson 3.3, loops:
looprepeats untilbreak,whilerepeats while a question stays true,forvisits each item once.continueskips the rest of one turn. - Lesson 3.4, reading a line:
read_lineadds what you typed, Enter key included, to a String. So youtrimit before comparing, clear it before the next read, and treat 0 bytes as "the input has ended".
Nothing new below. The questions are mixed on purpose: each one makes you work out which idea it is about before you can answer it, and that is what makes them stick. Commit to an answer before you open it.
Mixed quiz
Seven 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 E0308
- It compiles, then panics
Reveal the answer
It does not compile: E0308. An if that gives a value must give the same kind of value from every block, because label can only hold one kind. Here one road gives text and the other a number, so the compiler reports E0308, mismatched types. Revisit lesson 3.1, "if is an expression".
error[E0308]: `if` and `else` have incompatible types --> src/main.rs:3:52 | 3 | let label = if count == 1 { "message" } else { 2 }; | --------- ^ expected `&str`, found integer | | | expected because of this
What does this print?
3245
Reveal the answer
The program prints: 3 The empty message is skipped by continue and the loop goes on. hi, /help and yo are counted: this loop counts every non-empty line, commands included. At /quit, break leaves the loop, so late is never seen. Revisit lesson 3.3 for the difference between the two words.
The text below is exactly what read_line put in the String when someone typed /quit and pressed Enter. What does this print?
not a commandbye!bye! not a command
Reveal the answer
The program prints: not a command A text pattern matches exactly, and "/quit\n" has one more character than "/quit": the newline from pressing Enter. So the catch-all takes it. Calling typed.trim() before the match fixes it. Revisit lesson 3.4, and lesson 3.2 for exact matching.
What does this print?
too longa questionan echo
Reveal the answer
The program prints: too long An else if chain runs the first block whose question is true, from the top. WHAT? is 5 bytes, more than 4, so the length check wins and the question mark is never looked at. That is why Chatterbox's reply asks about length first. Revisit lesson 3.1.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0384
- Compile error E0308
Reveal the answer
It does not compile: E0384. A loop that counts down has to change its counter, and a name made with plain let can never change. The compiler reports E0384, cannot assign twice to an immutable variable, and suggests let mut. The loop in lesson 3.3 starts with let mut retries for exactly this reason (lesson 2.2 has the rule).
error[E0384]: cannot assign twice to immutable variable `retries` --> src/main.rs:5:9 | 2 | let retries = 3; | ------- first assignment to `retries` ... 5 | retries -= 1; | ^^^^^^^^^^^^ cannot assign twice to immutable variable | help: consider making this binding mutable | 2 | let mut retries = 3; | +++
Each push_str stands in for one read_line call. What does this print?
yohi yohi yo
Reveal the answer
The program prints: hi
yo Like read_line, push_str adds to what is already there, so after two turns the String holds both lines. trim only removes whitespace at the two ends, so the newline in the middle stays and two lines are printed. That is why Chatterbox calls line.clear() at the top of every turn. Revisit lesson 3.4.
Does this compile — and if it does, does it run?
- It compiles and runs
- Compile error E0004
- It compiles, then panics
Reveal the answer
It does not compile: E0004. A match must have an arm for every possible value, and a bool has two. The compiler reports E0004, non-exhaustive patterns, and names the one you forgot: false. It does not matter that quiet happens to be false here; the check is about every value it could hold. Revisit lesson 3.2.
error[E0004]: non-exhaustive patterns: `false` not covered --> src/main.rs:3:11 | 3 | match quiet { | ^^^^^ pattern `false` not covered | = note: the matched value is of type `bool` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 4 ~ true => println!("(quiet)"), 5 ~ false => todo!(), |
The milestone: a whole chat in one function
Now put the four ideas to work together. run_session plays a complete conversation the way Chatterbox does, except that the keyboard is replaced by one piece of text with a line per thing typed, exactly as in lesson 3.4's exercise. It returns everything Chatterbox would print. The rules, in order:
- Trim every line before looking at it.
- The first line is your name. Answer it with
hello,, the name and!. If the name line is empty, call the personfriend. - After that, an empty line is skipped without a word.
/helpprints theHELPtext./quitprintsbye,, the name and!, and nothing after it is read. Anything else is a message: it is counted and answered byreply. - At the very end, whether the chat stopped at
/quitor the input simply ran out, print how many messages there were:1 messagefor one, and0 messages,2 messagesand so on otherwise. Commands are never counted.
reply is the chain from lesson 3.1 with shorter answers and a smaller limit of 20 bytes, so the tests stay readable. A message of exactly 20 bytes is not too long.
Write reply and run_session. Every line you add to the output ends with a newline. The program in main plays a short chat with Sam; the hidden tests try longer ones, with empty names, stray spaces, no /quit at all, and exactly one message.
Write reply and run_session. Every line you add to the output ends with a newline. The program in main plays a short chat with Sam; the hidden tests try longer ones, with empty names, stray spaces, no /quit at all, and exactly one message.
Hidden tests (8): a_long_question_is_too_long_first a_whole_chat_with_sam commands_are_answered_but_not_counted every_line_is_trimmed an_empty_name_becomes_friend reply_follows_the_chain the_input_can_end_without_quit exactly_the_limit_is_allowed
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 have written in one function everything Chatterbox 0.3 does with its input: a choice with if, a table of commands with match, a loop that skips and stops, and lines that are trimmed before anyone looks at them. The next chapter does not add a command first. It opens the lid on something you have been using all along without seeing it: the String that holds what you type grows to any size, and chapter 4 shows where that text lives in memory and who is in charge of it.
Say it out loud, then reveal
You have one value and five different things that could happen depending on it. if or match, and why?Reveal
match: one value, several arms, checked top to bottom, and the compiler makes sure every possible value has somewhere to go. if is the tool for a single yes-or-no question.What is the difference between break and continue?Reveal
break leaves the whole loop at once. continue skips the rest of the current turn and carries on with the next one.Name two things you must do with a line read from the keyboard before you compare it, and say why.Reveal
read_line adds to what is there instead of replacing it.Why can a function body be a single if / else if / else chain with no return?Reveal
if is an expression: the chosen block's last line is the value of the whole if, and a function gives back the value of its last expression.Your Chatterbox so far: v0.3 (after lesson 3.4)
Download this version (zip)Unzip, then cargo run inside the folder.
That is lesson 3.5. Tick it off so the course remembers where you are. That is also the end of chapter 3: 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 3 of the Rust course cover?
Making decisions and repeating work: if and else, match with a catch-all arm, the three loops loop, while and for with break and continue, and reading a line from the keyboard with read_line. Together they turn Chatterbox into a chat program that answers what you type.
Why does a match on text need a catch-all arm?
Rust requires every possible value to have an arm, and text can be anything. A last arm with a bare name or an underscore covers everything the other arms did not, otherwise the compiler stops with error E0004.
Why does my typed command not match even though it looks right?
read_line keeps the newline from the Enter key at the end of the text, so the line is one character longer than the pattern. Call trim() on it before the match.
Can I skip the review and go straight to chapter 4?
You can, but the quiz takes a few minutes and tells you which lesson to reread if an answer surprised you. Chapter 4 builds on loops and match in every program it shows.
