Enums & Pattern Matching
An enum defines a type that can be one of several named variants, and match forces you to handle every one of them — the compiler itself will refuse to build if you forget a case.
Defining an enum
enum TrafficLight { Red, Yellow, Green, } fn main() { let light = TrafficLight::Red; match light { TrafficLight::Red => println!("Stop"), TrafficLight::Yellow => println!("Slow down"), TrafficLight::Green => println!("Go"), } }
Stop
match compares light against each listed pattern in order and runs the matching arm. Unlike a C switch, there's no fallthrough between arms and no need for a break.
match must be exhaustive
match light { TrafficLight::Red => println!("Stop"), TrafficLight::Green => println!("Go"), }
error[E0004]: non-exhaustive patterns: `TrafficLight::Yellow` not covered
|
| match light {
| ^^^^^ pattern `TrafficLight::Yellow` not coveredLeaving out Yellow is a compile error, not a bug you find later at runtime. This is one of the most quietly powerful features in Rust: add a new variant to an enum anywhere in a large codebase, and every match on that enum that doesn't already handle it stops compiling until you deal with it — impossible to accidentally forget a case.
Enum variants can carry data
enum Shape { Circle(f64), Rectangle(f64, f64), } fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(radius) => std::f64::consts::PI * radius * radius, Shape::Rectangle(w, h) => w * h, } } fn main() { let c = Shape::Circle(2.0); let r = Shape::Rectangle(3.0, 4.0); println!("{:.2}", area(&c)); println!("{}", area(&r)); }
12.57 12
Each Shape variant carries exactly the data that variant needs — a circle needs one number, a rectangle needs two. Matching on the enum both identifies which variant you have and unpacks its data into named bindings (radius, w, h) in one step.
if let for the single-case shortcut
When you only care about one specific variant and want to ignore the rest, if let avoids writing a full match with a throwaway catch-all arm:
fn main() { let shape = Shape::Circle(5.0); if let Shape::Circle(radius) = shape { println!("It's a circle with radius {}", radius); } }
It's a circle with radius 5
Option<T>, is built from exactly this pattern — a value that's either Some(x) or None. You'll meet it properly, along with Result, in the Error Handling lesson; the match and if let skills from this lesson apply directly.