In Rust, `enums` (enumerations) are custom data types that allow you to define a type by enumerating its possible variants. This means an enum value can be one of a defined set of possibilities. Enums are incredibly powerful because their variants can optionally hold data, similar to tuple structs or regular structs, making them highly versatile for modeling complex states or types. For example, an `enum` might represent the different states of a traffic light (Red, Yellow, Green) or various types of messages a program can receive.
`Pattern matching` is a powerful feature in Rust used to compare a value against a series of patterns and then execute code based on which pattern the value matches. The primary way to perform pattern matching is with the `match` control flow operator. A `match` expression takes a value and compares it against each pattern in turn. When a pattern matches, the corresponding code block is executed. `match` expressions are exhaustive, meaning you must cover every possible value that the type being matched can take. If you don't want to cover all possibilities, you can use a special `_` (underscore) pattern as a catch-all.
When combined, enums and pattern matching provide a robust and safe way to handle different types of data or states within your program. You define an enum to represent a set of distinct possibilities, and then you use `match` to safely and explicitly handle each of these possibilities, often extracting data stored within the enum variants in the process. This approach helps prevent bugs by ensuring you've considered all cases, and the compiler will enforce this exhaustiveness.
Rust also provides `if let` as a convenient way to handle a single successful match case without having to write a full `match` expression for all other cases. It's syntactic sugar for a `match` that only cares about one pattern and ignores the rest.
Example Code
```rust
// 1. Define an Enum: Represents different types of messages
// Variants can hold different kinds of data (no data, tuple data, struct data)
enum Message {
Quit, // No data
Move { x: i32, y: i32 }, // Anonymous struct data
Write(String), // Single String tuple data
ChangeColor(i32, i32, i32), // Three i32 tuple data
}
// A function that processes different messages using pattern matching
fn process_message(msg: Message) {
match msg { // The 'match' keyword starts the pattern matching block
Message::Quit => { // Pattern: Message::Quit
println!("The Quit message was received. Program exiting.");
}
Message::Move { x, y } => { // Pattern: Message::Move, extracts 'x' and 'y'
println!("Move to x: {} y: {}.", x, y);
}
Message::Write(text) => { // Pattern: Message::Write, extracts 'text'
println!("Writing message: {}", text);
}
Message::ChangeColor(r, g, b) => { // Pattern: Message::ChangeColor, extracts r, g, b
println!("Changing color to R:{}, G:{}, B:{}.", r, g, b);
}
}
}
// Another enum for demonstrating 'if let'
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
#[derive(Debug)] // This allows us to print the UsState enum for debugging
enum UsState {
Alabama,
Alaska,
// ... many more states
California,
Texas,
}
fn main() {
// Create instances of the Message enum
let quit_message = Message::Quit;
let move_message = Message::Move { x: 10, y: 20 };
let write_message = Message::Write(String::from("Hello Rustaceans!"));
let color_message = Message::ChangeColor(255, 128, 0);
// Process the messages using our function, which uses pattern matching
println!("\n--- Processing Messages with match ---");
process_message(quit_message);
process_message(move_message);
process_message(write_message);
process_message(color_message);
// Example of 'if let' for a specific enum variant
println!("\n--- Demonstrating if let ---");
let coin = Coin::Quarter(UsState::Alaska);
let other_coin = Coin::Dime;
if let Coin::Quarter(state) = coin { // 'if let' only executes if coin is a Quarter
println!("State quarter from {:?}!", state);
} else {
println!("This is not a quarter.");
}
if let Coin::Quarter(state) = other_coin { // This block will not execute
println!("This should not print for a Dime: {:?}!", state);
} else {
println!("This is not a quarter (it's a {:?}).", other_coin);
}
// You can also use 'match' with an underscore for exhaustiveness
println!("\n--- Match with Catch-all ---");
let some_value = Some(5);
let none_value: Option<i32> = None;
match some_value {
Some(val) => println!("Got a value: {}", val),
_ => println!("Got nothing (or something else we don't care about)."), // Catch-all for other cases
}
match none_value {
Some(val) => println!("Got a value: {}", val),
_ => println!("Got nothing (or something else we don't care about)."),
}
}
// Helper to print Coin enum (for the 'if let' else branch)
impl std::fmt::Debug for Coin {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Coin::Penny => write!(f, "Penny"),
Coin::Nickel => write!(f, "Nickel"),
Coin::Dime => write!(f, "Dime"),
Coin::Quarter(state) => write!(f, "Quarter from {:?}", state),
}
}
}
```








Enums and Pattern Matching