Pattern matching is a way to match the structure of a value and bind variables to its parts. It is a powerful way to handle data and control flow of a Rust program.
We generally use the match
expressions when it comes to pattern matching.
The syntax of the match
expressions is:
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
Here, PATTERN => EXPRESSION
are called patterns, a special syntax in Rust which usually works together with the match
keyword.
Matching a Variable in Rust
We can pattern match against the value of a variable. This is useful if our code wants to take some action based on a particular value. For example,
fn main() {
let x = 2;
// use of match expression to pattern match against variable x
match x {
1 => println!("x is 1"),
2 => println!("x is 2"),
_ => println!("x is something else"),
}
}
Output
x is 2
Here, we have used the match
expression to match against x. In the match
body, we pattern matched with values 1
, 2
and _
.
1 => println!("x is 1"),
2 => println!("x is 2"),
_ => println!("x is something else"),
Because the value of x is 2
, the pattern that matches is:
2 => println!("x is 2")
Thus, x is 2 is printed on the screen.
Notice that we also match against underscore _
. The _
has a special meaning in pattern matching, if all the other patterns do not match, it defaults to _
.
Note: match
body (also known as match arms) should always ensure that all possible cases are being handled. If all possible cases are not handled, the Rust program fails to compile.
Matching an Enum In Rust
Pattern matching is extensively used to match enum variants. For example,
fn main() {
enum Color {
Red,
Green,
Blue,
}
let my_color = Color::Green;
// use of match expression to match against an enum variant
match my_color {
Color::Red => println!("The color is red"),
Color::Green => println!("The color is green"),
Color::Blue => println!("The color is blue"),
}
}
Output
The color is green
Here, we created a pattern in the match
expression to match against all enum variants.
Color::Red => println!("The color is red"),
Color::Green => println!("The color is green"),
Color::Blue => println!("The color is blue"),
Because the value of my_color
is Color::Green
, the pattern that it matches is:
Color::Green => println!("The color is green"),
Thus, The color is green
is printed on the screen.
Matching Option and Result Type in Rust
The most common case for pattern matching is with Option
and Result
enum types. Both the Option
and Result
type have two variants.
Option
type has:
None
→ to indicate failure with no valueSome(T)
→ a value with type T
Result
type has:
Ok(T)
→ operation succeeded with value TErr(E)
→ operation failed with an error E
Let's look at examples of how we can use pattern matching on these types.
Example: Matching Option Type in Rust
fn main() {
let my_option: Option<i32> = Some(222);
// use of match expression to match Option type
match my_option {
Some(value) => println!("The option has a value of {}", value),
None => println!("The option has no value"),
}
}
Output
The option has a value of 222
In this example, my_option
is an Option
type that contains either a Some
variant with an i32
value or a None
variant.
The match
expression compares the value of my_option
to the Some
and None
variants, and binds the value of Some
variant to the value
variable.
When a match is found, the corresponding code block is executed.
Some(value) => println!("The option has a value of {}", value),
Thus, The option has a value of 222
is printed on the screen.
Example: Matching Result Type in Rust
fn main() {
let my_result: Result<i32, &str> = Ok(100);
// use of match expression to match Result type
match my_result {
Ok(value) => println!("The result is {}", value),
Err(error) => println!("The error message is {}", error),
}
}
Output
The result is 100
In this example, my_result
is a Result
type that contains either an Ok
variant with an i32
value, or an Err
variant with an error message of type &str
.
The match expression compares the value of my_result
to the Ok
and Err
variants, and binds the value of Ok
variant to the value
variable or the Err
variant to the error
variable.
Ok(value) => println!("The result is {}", value),
Err(error) => println!("The error message is {}", error),
When a match is found, the corresponding code block is executed.
Ok(value) => println!("The result is {}", value),
Thus, The result is 100
is printed on the screen.
if let Expression in Rust
The if let
expression in Rust is a shorthand for a match
expression with only one pattern/arm to match.
It allows us to match on a value and then execute code only if the match is successful.
fn main() {
let my_option: Option<i32> = Some(222);
// use of if let expression on the Option type
if let Some(value) = my_option {
println!("The option has a value of {}", value);
} else {
println!("The option has no value");
}
}
Output
The option has a value of 111
Here, the if let
expression is matching on the my_option
variable and binding the value of Some
variant to the value
variable.
If the match is successful, the code inside the if
block is executed. If the match is not successful, the code inside the else
block is executed.
Common Use Cases of Pattern Matching in Rust
As you have seen, pattern matching is useful in numerous situations. Some common use cases for pattern matching include:
- Matching against any value like integers
- Matching against enum, struct or tuples
- Expressing conditional logic
- Destructuring data structure or destructuring elements of an iterator in a for loop
- Error handling with Result and Option types