In our previous post, we explored how to model custom data using Enums, allowing a value to be exactly one of several distinct variants. But once you have data locked inside an Enum, how do you extract it and make decisions based on it?
You could use a long, messy chain of if and else if statements. However, Rust provides a tool that is elegant and powerful, it will change how you write logic, the match operator.
Let’s set up a new project to see it in action. Open your terminal and run:
cargo new pattern_matching
cd pattern_matching
The Evolution of if/else
At its core, match allows you to compare a value against a set of patterns and execute code based on which pattern matches. It is similar to a switch statement in C++ or JavaScript.
Let’s look at a basic example. Open your src/main.rs file and add this:
fn main() {
let role = "DPS";
match role {
"Tank" => println!("Focus on holding aggro and damage mitigation."),
"Controller" => println!("Keep the group's power topped off."),
"Healer" => println!("Keep the group alive."),
"DPS" => println!("Maximize your damage output and clip animations!"),
_ => println!("Unknown role selected."),
}
}
The Syntax Breakdown
The Target: We give
matcha variable to look at (in this case,role).The Arms: Each line inside the block is an “arm.” An arm consists of a pattern (e.g.,
"DPS"), the=>operator, and the expression to run if the pattern matches.The Catch-All (
_): The underscore is a special pattern that matches everything else. Becauseroleis a string, and strings can contain infinite possibilities, Rust demands we provide a fallback option just in case the string is “Potato” instead of a valid role.
The True Power: Matching Enums
Where match truly shines is when it is paired with Enums.
Imagine you are building a combat system and need to handle the activation of different specialized items. Let’s model some artifacts that trigger unique effects, like the Omega Beams or a Transformation Sword.
Update your src/main.rs with the following code:
enum Artifact {
OmegaBeams,
TransformationSword,
Strategist,
}
fn activate_artifact(active_item: Artifact) {
match active_item {
Artifact::OmegaBeams => {
println!("Triggering Omega Beams!");
println!("Increasing power!");
}
Artifact::TransformationSword => {
println!("Critical hit chance increased.");
}
Artifact::Strategist => {
println!("Critical hits now apply a damage-over-time effect.");
}
}
}
fn main() {
let my_artifact = Artifact::OmegaBeams;
activate_artifact(my_artifact);
}
If you run cargo run, it will output:
Triggering Omega Beams!
Increasing power!
(Note: In the OmegaBeams arm, we used curly braces {}. If your execution logic requires multiple lines of code, you just wrap it in a standard block!)
The Exhaustiveness Guarantee
Delete the Artifact::Strategist arm from the match block and try running cargo build.
The compiler will throw a hard error: pattern \Strategist not covered.
This is Rust’s exhaustiveness check. When matching against an Enum, the compiler forces you to handle every single possible variant. This eliminates an entire class of bugs where a developer adds a new Enum variant later on (like adding a new powerset to the game) but forgets to update the logic that handles it. Rust simply will not compile until you have accounted for the new data.
Matching and Extracting Data
Enums in Rust have a superpower we haven’t covered yet: they can actually store data inside their variants.
Let’s say we have a Powerset enum. Some powersets might just be basic identifiers, but maybe the Mental powerset variant needs to store data about whether an invisibility loadout is currently active.
enum Powerset {
Electric,
Mental(bool), // This variant holds a boolean value!
}
fn analyze_loadout(power: Powerset) {
match power {
Powerset::Electric => println!("Standard DPS rotation ready."),
// We extract the inner boolean value into a variable named 'is_invisible'
Powerset::Mental(is_invisible) => {
if is_invisible {
println!("Stealth rotation active. Surprise attack ready!");
} else {
println!("Standard mental rotation active.");
}
}
}
}
fn main() {
let my_power = Powerset::Mental(true);
analyze_loadout(my_power);
}
When the match hits the Powerset::Mental arm, it “unwraps” the data stored inside the enum and binds it to a local variable (is_invisible), which you can immediately use inside that block of code.
Summary
The match operator is a cornerstone of idiomatic Rust.
It is cleaner than chained
if/elsestatements.It is exhaustive, forcing you to handle edge cases and preventing unhandled states.
It allows you to extract internal data smoothly from complex Enums.
Once you get comfortable with pattern matching, writing robust, bug-free logic feels less like coding and more like putting puzzle pieces exactly where they belong.
Rust: Setting up your development environment
Rust: Cargo and the Anatomy of “Hello, World”
Rust: Variables and Immutability
Rust: Control Flow, Making Decisions with If, Else, and Loops
Rust: Ownership Explained, Memory Safety
Rust: Structs and Enums, Modeling Custom Data
Rust: Pattern Matching, Mastering the Match Operator
Rust: Error Handling, Ditching Exceptions for Result and Option
Rust: Collections, Mastering Vectors and Strings
Rust: Modules and Traits, Organizing Code and Shared Behavior