Vibe Engines
YouTube

Lesson 3.2 · 9 min

Choosing with match

An if picks between two roads. Chatterbox needs more: /help should show help, /quit should say goodbye, and everything else should be echoed. For many roads Rust gives you match.

Adds to ChatterboxCommands: /help, /quit.

Arms

A match takes one value and lays out a list of arms. Each arm is a pattern, an arrow =>, and what to do. Rust tries the arms from the top and runs the first one that fits.

Three arms, the last one matches anything
fn main() {
let message = "/help";
match message {
"/help" => println!("commands: /help, /quit"),
"/quit" => println!("bye!"),
other => println!("you: {other}"),
}
}
$ cargo run
commands: /help, /quit

The first two arms are exact pieces of text. The last arm is just a name, other. A bare name matches anything, and gives that name to the value so you can use it. That is what makes it the "everything else" arm. Try "hello" as the message.

Why the last arm is not optional

Rust makes you cover every possible value. Text can be anything, so with only two arms, what should happen for "hello"? The compiler will not let you leave that undecided:

No arm for "everything else"
fn main() {
let message = "hello";
match message {
"/help" => println!("commands: /help, /quit"),
"/quit" => println!("bye!"),
}
}
error[E0004]: non-exhaustive patterns: `&_` not covered
 --> src/main.rs:3:11
  |
3 |     match message {
  |           ^^^^^^^ pattern `&_` not covered
  |
  = note: the matched value is of type `&str`
  = note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary
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 ~         "/quit" => println!("bye!"),
6 ~         &_ => todo!(),
  |

"non-exhaustive patterns" is the compiler saying: there are values you did not handle. It even suggests a catch-all arm (it prints &_ here, because the value is a &str; _ or a name like other do the same job). The underscore is a catch-all without a name, for when you do not need the value.

This is a gift, not a nuisance. In a chat program that grows a new command next month, the compiler will point at every match that forgot it.

PredictWhich arm fires?
fn main() {
let message = "/Help";
match message {
"/help" => println!("help"),
"/quit" => println!("quit"),
other => println!("echo {other}"),
}
}

What does this print?

  • help
  • echo /Help
  • quit
Reveal the answer

The program prints: echo /Help Text patterns match exactly, capital letters included. "/Help" is not "/help", so the first two arms are skipped and the catch-all takes it.

Chatterbox v0.3, commands

The empty-message check from last lesson becomes one more arm, "", which is neater than an if next to a match. Commands do not count as messages, so only the catch-all arm touches message_count. Notice an arm can be a block in braces when it needs more than one line.

src/main.rs: Chatterbox v0.3
fn main() {
let name = "Sam";
let bot = "Chatterbox";
let version = "0.3";
let mut message_count = 0;
 
println!("{bot} v{version}");
println!("Hi {name}! I'm {bot}.");
 
let message = "/help";
match message {
"" => println!("(nothing to say)"),
"/help" => println!("commands: /help, /quit"),
"/quit" => println!("bye, {name}!"),
other => {
message_count += 1;
println!("you: {other}");
}
}
println!("messages so far: {message_count}");
}
$ cargo run
Chatterbox v0.3
Hi Sam! I'm Chatterbox.
commands: /help, /quit
messages so far: 0
Try itYour turn: the command table

Finish handle: /help gives commands: /help, /quit, /quit gives bye!, an empty message gives (nothing to say), and anything else gives you: plus the text.

Finish handle: /help gives commands: /help, /quit, /quit gives bye!, an empty message gives (nothing to say), and anything else gives you: plus the text.

fn handle(message: &str) -> String {
// use match with one arm per command and a catch-all
String::new()
}
 
fn main() {
println!("{}", handle("/help"));
}

Hidden tests (4): empty_is_ignored help_lists_commands other_text_is_echoed quit_says_bye

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 a bare name like other do as a match arm?Reveal
It matches any value, and gives that value the name so you can use it.
What does "non-exhaustive patterns" (E0004) mean?Reveal
Some possible values have no arm. Add a catch-all arm or the missing case.
Does "/Help" match the arm "/help"?Reveal
No. Text patterns must match exactly, capital letters included.

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

Learn Rust by building a chat app · Lesson 3.2 · every compiler message above is real output from rustc 1.99.0 (b940084d7 2026-09-28) (edition 2024) · Vibe Engines · 2026