Pattern Matching using Match Construct
Sivakumar
Posted on May 1, 2023
In this blog post, let us see one of a powerful feature of Rust match
. Rust has an extremely powerful control flow construct called match
that allows you to compare a value against a series of patterns and then execute code based on which pattern matches. Patterns can be made up of literal values, variable names, wildcards, and many other things. The power of match
comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled.
Syntax
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
Matching Literals
match
construct can be used to check literals.
let x = 10;
match x {
10 => println!("Ten"),
20 => println!("Twenty"),
30 => println!("Thirty"),
_ => println!("Others"),
}
Matching Named Variables
match
construct starts a new scope variables declared as part of a pattern inside the match expression will shadow those with the same name outside the match construct, as is the case with all variables. In the below example, we declare a variable named a with the value Some(1) and a variable b with the value 2. We then create a match expression on the value a.
let x = Some(1);
let y = 2;
match x {
Some(10) => println!("We got the value 10"),
Some(y) => println!("Matched, y = {y}"),
_ => println!("Default case, x = {:?}", x),
}
println!("Out of the match construct: x = {:?}, y = {y}", x);
The above code produces below output
We got the value 1
Out of the match construct: x = Some(1), y = 2
Matching Multiple Patterns
match
construct supports matching multiple patterns using the |
syntax, which is the pattern or operator. Lets check this below example
let a = 10;
match a {
10 | 20 => println!("Ten or Twenty"),
3 => println!("Thirty"),
_ => println!("Not in the range"),
}
In the above code we match the value of a
against the match arms, the first of which has an or option, meaning if the value of a
matches either of the values in that arm. The above code produces below output
Ten or Twenty
Matching Ranges of Values with ..=
match
construct supports the ..=
syntax which allows us to match to an inclusive range of values.
let a = 10;
match a {
1..=9 => println!("Between one to nine"),
10..=20 => println!("Between ten to twenty"),
_ => println!("Not in the range"),
}
In the above code, when a pattern matches any of the values within the given range, that arm will execute. The above code produces below output
Between ten to twenty
Matching Structs
match
construct also works with struct
. In the below code, we have a Point struct
with two fields named x
and y
. When the match
arm matches any or all of the values, the corresponding arm will get executed. In the below example, we've a point struct with field values x=0
and y=10
hence the 2nd match arm Point { x: 0, y }
gets executed.
let p = Point { x: 0, y: 10 };
match p {
Point { x, y: 0 } => println!("On the x axis at {x}"),
Point { x: 0, y } => println!("On the y axis at {y}"),
Point { x, y } => {
println!("On neither axis: ({x}, {y})");
}
}
The above code produces below output
On the y axis at 10
Matching Enums
match
construct also works with enum
like struct
. We can deconstruct enum
using match
construct. Let us see an example below
enum Message {
Exit,
Point { x: i32, y: i32 },
Print(String),
RGB (i32, i32, i32),
}
let m = Message::Print(String::from("Hello World"));
match m {
Message::Exit => {
println!("Exit Enum");
}
Message::Point { x, y } => {
println!("Point with x axis {x} and y axis {y}");
}
Message::Print(text) => {
println!("Printing message: {text}");
}
Message::RGB(r, g, b) => {
println!("Red {r}, Green {g}, and Blue {b}",)
}
}
In the above code, we have Message enum with Print variant. When we deconstruct this enum using match
, the corresponding arm gets executed and produces below output
Printing message: Hello World
Ignoring Values in Match Pattern
We've seen that it’s sometimes useful to ignore values in a pattern, such as in the last arm of a match, to get a catchall that doesn’t actually do anything but does account for all remaining possible values. There are a few ways to ignore entire values or parts of values in a pattern: using the _ pattern, using the _ pattern within another pattern, using a name that starts with an underscore, or using .. to ignore remaining parts of a value. Let’s explore how and why to use each of these patterns.
Ignoring an Entire Value with _
It is possible to ignore entire value using _
. In the below example, we have two variables a
and b. We're checking if the values of
bhas an exact match ignoring whatever the values of
a`.
`
let a = 5;
let b = 10;
match (a, b) {
(_, 10) => println!("Ignored a but matched b which is having value 10"),
_ => println!("All othe cases")
}
`
Ignoring Remaining Parts of a Value with ..
We can use ..
to ignore remaining parts of value in the match
.
`
let x = 1;
let y = 2;
let z = 3;
match (x, y, z) {
(1, ..) => println!("x is 1 but ignored the other values"),
_ => println!("All other cases")
}
`
Extra Conditionals with Match Guards
A match guard is an additional if condition, specified after the pattern in a match arm, that must also match for that arm to be chosen. Match guards are useful for expressing more complex ideas than a pattern alone allows.
`
let number = 50;
match number {
x if x % 10 == 0 => println!("{x} is divisable by 10"),
x if x % 3 == 0 => println!("{x} is divisable by 3"),
_ => println!("All other cases")
}
`
In the above code example, we've match arm with extra condition to test more specific case.
Hope this blog post gives you detailed insight of enum
construct. You can get the code samples from this link
Please feel free to share your comments if any
Happy reading!!!
Posted on May 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.