impl enum / Pattern Branching with match
| Since: | Rust 1.0(2015) |
|---|
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
sample_enum_impl_match.rs
#[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());
}
Compile with the following command:
rustc enum_impl_match.rs ./enum_impl_match Red: 60 secs, can go?: false Next: Green Value: 42 Positive:1 Negative:-3 Positive:5 Negative:-2 Positive:8 (3 + 4) * (-2) = -14
Sample Code 2: Match Guards and Nested Enums
This example uses match guards (if guard) and nests one enum inside another.
enum_impl_match2.rs
#[derive(Debug)]
enum Status {
Active,
Inactive,
Suspended(String),
}
#[derive(Debug)]
struct Character {
name: String,
level: u32,
status: Status,
}
impl Character {
fn new(name: &str, level: u32, status: Status) -> Self {
Character {
name: name.to_string(),
level,
status,
}
}
fn describe(&self) -> String {
match &self.status {
Status::Active if self.level >= 50 => {
format!("{}: Elite member (Lv.{})", self.name, self.level)
}
Status::Active => {
format!("{}: Active (Lv.{})", self.name, self.level)
}
Status::Inactive => {
format!("{}: Retired", self.name)
}
Status::Suspended(reason) => {
format!("{}: Suspended - {}", self.name, reason)
}
}
}
fn is_available(&self) -> bool {
matches!(&self.status, Status::Active)
}
}
fn main() {
let members = vec![
Character::new("Yagami Iori", 80, Status::Active),
Character::new("Kusanagi Kyo", 75, Status::Active),
Character::new("Terry Bogard", 30, Status::Active),
Character::new("Mai Shiranui", 60, Status::Inactive),
Character::new("Rugal Bernstein",99, Status::Suspended("missing".to_string())),
];
for m in &members {
println!("{}", m.describe());
}
println!("\nAvailable members:");
for m in members.iter().filter(|m| m.is_available()) {
println!(" - {}", m.name);
}
}
Compile with the following command:
rustc enum_impl_match2.rs ./enum_impl_match2 Yagami Iori: Elite member (Lv.80) Kusanagi Kyo: Elite member (Lv.75) Terry Bogard: Active (Lv.30) Mai Shiranui: Retired Rugal Bernstein: Suspended - missing Available members: - Yagami Iori - Kusanagi Kyo - Terry Bogard
Sample Code 3: Multiple Methods in an impl Block
Adding multiple methods to an enum and using match to return computed values.
enum_impl_match3.rs
#[derive(Debug, Clone, PartialEq)]
enum FightStyle {
Striker,
Grappler,
Shooter,
AllAround,
}
impl FightStyle {
fn name(&self) -> &str {
match self {
FightStyle::Striker => "Striker",
FightStyle::Grappler => "Grappler",
FightStyle::Shooter => "Shooter",
FightStyle::AllAround => "All-Around",
}
}
fn power_rating(&self) -> u32 {
match self {
FightStyle::Striker => 90,
FightStyle::Grappler => 80,
FightStyle::Shooter => 70,
FightStyle::AllAround => 85,
}
}
fn counters(&self) -> FightStyle {
match self {
FightStyle::Striker => FightStyle::Grappler,
FightStyle::Grappler => FightStyle::Shooter,
FightStyle::Shooter => FightStyle::Striker,
FightStyle::AllAround => FightStyle::AllAround,
}
}
}
fn main() {
let styles = [
("Yagami Iori", FightStyle::Striker),
("Leona", FightStyle::AllAround),
("Ralf Jones", FightStyle::Grappler),
("Clark Still", FightStyle::Grappler),
("Billy Kane", FightStyle::Shooter),
];
for (name, style) in &styles {
let counter = style.counters();
println!(
"{}: {} (power:{}) → weakness: {}",
name, style.name(), style.power_rating(), counter.name()
);
}
}
Compile with the following command:
rustc enum_impl_match3.rs ./enum_impl_match3 Yagami Iori: Striker (power:90) → weakness: Grappler Leona: All-Around (power:85) → weakness: All-Around Ralf Jones: Grappler (power:80) → weakness: Shooter Clark Still: Grappler (power:80) → weakness: Shooter Billy Kane: Shooter (power:70) → weakness: Striker
Common Mistakes
Mistake 1: Using self instead of &self, causing ownership to move
Using self in a method signature moves ownership into the method, making the value unusable afterward. Use &self to borrow instead.
ng_self.rs
#[derive(Debug)]
enum Light { Red, Green }
impl Light {
fn label(self) -> &'static str {
match self {
Light::Red => "red",
Light::Green => "green",
}
}
}
fn main() {
let light = Light::Red;
println!("{}", light.label());
println!("{:?}", light);
}
Compile with the following command:
rustc ng_self.rs error[E0382]: borrow of moved value: `light`
Correct version:
ok_self.rs
#[derive(Debug)]
enum Light { Red, Green }
impl Light {
fn label(&self) -> &'static str {
match self {
Light::Red => "red",
Light::Green => "green",
}
}
}
fn main() {
let light = Light::Red;
println!("{}", light.label());
println!("{:?}", light);
}
Running the above produces the following output:
rustc ok_self.rs && ./ok_self red Red
Mistake 2: Defining a method outside the impl block
Methods on an enum must be defined inside an impl block. Writing a method outside causes a compile error.
ng_impl.rs
enum Direction { North, South }
fn Direction::label(&self) -> &str {
"north"
}
Compile with the following command:
rustc ng_impl.rs error: expected item, found `::`
Correct version:
ok_impl.rs
enum Direction { North, South }
impl Direction {
fn label(&self) -> &str {
match self {
Direction::North => "north",
Direction::South => "south",
}
}
}
Running the above produces the following output:
rustc ok_impl.rs && ./ok_impl
Mistake 3: Forgetting to cover all variants in match
A match expression must cover every possible variant. When you add a new variant and forget to update an existing match, the compiler reports an error.
ng_match.rs
enum Color { Red, Green, Blue }
fn describe(c: Color) -> &'static str {
match c {
Color::Red => "red",
Color::Green => "green",
}
}
Compile with the following command:
rustc ng_match.rs error[E0004]: non-exhaustive patterns: `Blue` not covered
Correct version (add the Blue arm or handle remaining variants with a wildcard):
ok_match.rs
enum Color { Red, Green, Blue }
fn describe(c: Color) -> &'static str {
match c {
Color::Red => "red",
Color::Green => "green",
Color::Blue => "blue",
}
}
Running the above produces the following output:
rustc ok_match.rs && ./ok_match
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.