Pattern matching in Rust implemented with match
statement, which contains list of pattern to
expression pairs called arms
.
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
match
arms must contain all possible variants of execution. There will be a compilation error
if there is some case of exectution which not specified as a match
arm.
One solution to use _
pattern, which allows to gather all possible variants, not specified
directly.
if let
Another solution for situations when we need to catch only one pattern case is to use if let
construct.
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
Here important to notice, that let if
as well as match
can create shadowing varable, like in
line } else if let Ok(age) = age {
. This means that further if age > 30
has the value from Ok(age)
and not original age
. And also this means that it is not possible to use age this way:
} else if let Ok(age) = age && age > 30{
.
while let
This expression is sililar to if let
, but here while
loop will run until pattern is matching.
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
for
for
loop in Rust also uses pattern matching. In particular, expression, following for
keyword
is a pattern.
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
let
Assignment in Rust is also a pattern. For example: let x = 5;
is let PATTERN = EXPRESSION;
.
Example, x
is a pattern that means “bind what matches here to the variable x.” Because the name
x
is the whole pattern, this pattern effectively means “bind everything to the variable x
,
whatever the value is.”
More complex example of it:
let (x, y, z) = (1, 2, 3);
Functional parameters also may be patterns:
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Matching literals:
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
Matching Named Variables:
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
Matching Multiple Patterns:
let x = 1;
match {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
Matching Ranges:
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("something else"),
}
Ranges are only allowed with numeric values or char values, because the compiler checks that the range isn’t empty at compile time.
let x = 'c';
match x {
'a'..='j' => println!("early ASCII letter"),
'k'..='z' => println!("late ASCII letter"),
_ => println!("something else"),
}
Conditional Pattern Matching
match
also supprts additional conditions:
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
Extra condition also solves shadowing problem, when match
creates new variable with same name
which can hide external scope variable:
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(n) if n == y => println!("Matched, n = {}", n),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {}", x, y);
}
With |
operator:
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
@ operator
@
allows to create new variable and test it on some pattern, but use it after in matched block:
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {}", id_variable),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {}", id),
}