We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Shape Your Data: Structs, Enums, Pattern Matching step 6 of 26
Enums are real sum types
Parse a list of command lines into an Event enum and render each one back
as a description.
pub fn describe_events(lines: Vec<String>) -> Vec<String>
| line | rendered as |
|---|---|
quit |
quit |
move 3 -4 |
move to (3, -4) |
write hello world |
write "hello world" |
color 255 0 128 |
color #ff0080 |
| anything else |
unknown |
Coordinates are i64; colour components are u8, so color 300 0 0 is not
a valid line. write with nothing after it produces the empty string.
This is the most important idea in the track
An enum in C, Java or Go is a set of named integers. An enum in Rust is a sum type: a value that is exactly one of several alternatives, and each alternative may carry its own data of its own shape.
enum Event {
Quit, // no data
Move { x: i64, y: i64 }, // named fields, like a struct
Write(String), // positional, like a tuple struct
ChangeColor(u8, u8, u8), // three positional fields
}
All four of those are one type. An Event is one of them — never two,
never none. There is no valid Event outside that list. That closed-ness is
what the rest of Rust’s design is built on, and it is why Option and
Result are ordinary library types rather than language features.
Compare the alternative you have almost certainly written before: a struct
with a kind string, four nullable fields, and a comment explaining which
combinations are legal. Every function that touches it has to re-check the
invariant, and the compiler helps with none of it. With a sum type the
illegal combinations are not “checked” — they are unrepresentable.
The three variant shapes
Beginners learn the unit shape (Quit) and stop. All three matter:
-
Quit— a bare tag. -
Write(String)— a tuple variant. The variant name is also a function:Event::Writehas typefn(String) -> Event, so.map(Event::Write)works. -
Move { x: i64, y: i64 }— a struct variant, when positional arguments would be ambiguous.Move(i64, i64)gives you no way to know which is x.
Note the variants of an enum live in the enum’s namespace: you write
Event::Quit, not Quit. That namespacing is also why
enum_variant_names will reject AppleFruit, PearFruit, PlumFruit —
once the enum is called Fruit, repeating it in every variant is noise.
Reading one back out
You take a sum type apart with match, and a match on an enum must handle
every variant. The starter’s render is missing an arm; you will meet
E0004 and it will name the variant you forgot. Exhaustiveness gets an item
of its own next — for now, notice that the compiler is doing bookkeeping you
would otherwise be doing in your head.
About the parser
The starter hands you a parser. It matches on words.as_slice() with slice
patterns — ["move", x, y] means “exactly three words, the first of which is
move“. Slice patterns get a full item later in this track; for now read
them as the shapes they look like. The parser is complete apart from one
thing: it constructs a variant your enum does not have. Fix the enum.
Two harness facts worth knowing
#![allow(dead_code)] sits at the top of the file. Without it, any variant
the tests never construct would be a hard error under -D warnings
(variantMoveis never constructed), and you would be fighting the gate
instead of learning enums.
And format!("{text:?}") on a String prints it with the quotes and with
escapes applied — that is what the write rows expect.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.