impl enum / Pattern Branching with match
Rust enums support methods added via impl, and the match expression lets you handle every variant exhaustively. Because the compiler detects any unhandled variants, you get safe, reliable pattern matching.
Syntax
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle(f64, f64, f64),
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle(a, b, c) => {
let s = (a + b + c) / 2.0;
(s * (s - a) * (s - b) * (s - c)).sqrt() // Heron's formula
}
}
}
}
// if let: handles only one specific variant.
if let Shape::Circle(r) = shape {
println!("Circle radius: {}", r);
}
How to use match and if let
| Syntax | Description |
|---|---|
| match val { Variant(x) => ... } | Handles all variants exhaustively (a compile error occurs if any variant is missing). |
| _ => ... | Wildcard: catches all remaining variants in one arm. |
| if let Variant(x) = val { ... } | Handles only one specific variant. You can also use an else branch. |
| while let Variant(x) = val { ... } | Loops as long as the pattern matches. |
| Variant(x) if x > 0 => ... | Match guard: adds an extra condition after matching a variant. |
| Variant(x) | Variant2(x) => ... | Combines multiple variants into a single arm using |. |
| impl Enum { fn method(&self) } | Adds methods to an enum (same as with structs). |
Sample code
#[derive(Debug)]
enum TrafficLight {
Red,
Yellow,
Green,
}
impl TrafficLight {
fn duration_secs(&self) -> u32 {
match self {
TrafficLight::Red => 60,
TrafficLight::Yellow => 5,
TrafficLight::Green => 45,
}
}
fn can_go(&self) -> bool {
matches!(self, TrafficLight::Green)
}
fn next(&self) -> TrafficLight {
match self {
TrafficLight::Red => TrafficLight::Green,
TrafficLight::Green => TrafficLight::Yellow,
TrafficLight::Yellow => TrafficLight::Red,
}
}
}
#[derive(Debug)]
enum Expr {
Number(f64),
Add(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Neg(Box<Expr>),
}
impl Expr {
fn eval(&self) -> f64 {
match self {
Expr::Number(n) => *n,
Expr::Add(a, b) => a.eval() + b.eval(),
Expr::Mul(a, b) => a.eval() * b.eval(),
Expr::Neg(e) => -e.eval(),
}
}
}
fn main() {
// Call methods on an enum.
let light = TrafficLight::Red;
println!("{:?}: {} secs, can go?: {}", light, light.duration_secs(), light.can_go());
let next = light.next();
println!("Next: {:?}", next);
// Use if let to handle only one variant.
let value = Some(42);
if let Some(n) = value {
println!("Value: {}", n);
}
// Match guards with conditions.
let numbers = vec![1, -3, 5, -2, 8];
for &n in &numbers {
match n {
x if x > 0 => print!("pos:{} ", x),
x if x < 0 => print!("neg:{} ", x),
_ => print!("zero "),
}
}
println!();
// Evaluate an expression using a recursive enum.
// (3 + 4) * (-2) = -14
let expr = Expr::Mul(
Box::new(Expr::Add(
Box::new(Expr::Number(3.0)),
Box::new(Expr::Number(4.0)),
)),
Box::new(Expr::Neg(Box::new(Expr::Number(2.0)))),
);
println!("(3 + 4) * (-2) = {}", expr.eval());
}
Notes
In Rust, match expressions must cover every variant. Adding a new variant without updating existing match arms causes a compile error, which prevents unhandled cases from slipping through. Use a wildcard arm (_) to handle remaining variants together.
Recursive enums cause a compile error because the compiler cannot determine their size automatically. Wrap the recursive field in a pointer such as Box<Expr> to fix the size.
For details on defining enums and their variants, see enum / variant definitions.
If you find any errors or copyright issues, please contact us.