Enums and pattern matching in rust
Defining an enum
An enum (short for "enumeration") is a user-defined type that represents a set of named values. In Rust, you can define an enum using the enum
keyword.
Example:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
Enum values
An enum can have multiple values, each with its own unique name. These values are called "variants". In the example above, Penny
, Nickel
, and Dime
are the variants of the Coin
enum.
The Option Enum and Its Advantages Over Null Values
Rust's Option<T>
enum is a special type that represents either Some value or None. This allows you to explicitly handle the absence of a value, rather than relying on null values. Using Option<T>
can help prevent runtime errors and make your code more robust.
Example:
let maybe_int = Some(5);
match maybe_int {
Some(i) => println!("Value: {}", i),
None => println!("No value"),
}
The match Control Flow Construct
The match
control flow construct is used to execute different blocks of code based on the value of an expression. In Rust, you can use match
with enums and pattern matching to extract values from enum variants.
Example:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
let coin = Coin::Quarter(UsState::Alabama);
match coin {
Coin::Penny => println!("Penny!"),
Coin::Nickel => println!("Nickel!"),
Coin::Dime => println!("Dime!"),
Coin::Quarter(state) => println!("Quarter from {state:?}!"),
}
Patterns that bind to values
Patterns are used in match
expressions to bind values to variables. You can use patterns like literals, identifiers, and tuple structs to match enum variants.
Example:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
let coin = Coin::Quarter(UsState::Alabama);
match coin {
Coin::Penny => println!("Penny!"),
x => println!("Other: {x:?}"), // x binds to the value
}
Matching with Option
You can use Option<T>
in a match
expression to handle the absence of a value.
Example:
let maybe_int = Some(5);
match maybe_int {
Some(i) => println!("Value: {}", i),
None => println!("No value"),
}
Matches are exhaustive
Rust's match
expressions are exhaustive, meaning that every possible variant of an enum must be handled. If you don't handle all the variants, the compiler will error.
Example:
enum Coin {
Penny,
Nickel,
Dime,
}
let coin = Coin::Quarter(UsState::Alabama); // Error: Quarter is not handled!
match coin {
Coin::Penny => println!("Penny!"),
Coin::Nickel => println!("Nickel!"),
_ => println!("Other"), // Not exhaustive!
}
Catch-all Patterns and the _ Placeholder
The _
placeholder can be used as a catch-all pattern to handle any variant that doesn't match the previous patterns.
Example:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
let coin = Coin::Quarter(UsState::Alabama);
match coin {
Coin::Penny => println!("Penny!"),
x => println!("Other: {x:?}"), // x binds to the value
}
Concise control flow with if let
The if let
statement is a concise way to handle enums and pattern matching. It combines an if
statement with a match
expression.
Example:
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
let coin = Coin::Quarter(UsState::Alabama);
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {state:?}!");
} else {
count += 1;
}
Output:
State quarter from Alabama!