Rust Match Guards
A match guard is an extra if test on a match arm. A guard checks a value the shape cannot describe.
The long way: an if inside the arm
Without a guard, the extra test nests inside the arm:
let age = Some(15);
match age {
Some(a) => {
if a >= 18 {
println!("adult");
} else {
println!("minor, age {a}");
}
}
None => println!("no age given"),
}A guard adds a test a pattern cannot say
A pattern checks the shape. The guard after if checks a value. Here the shape matches but the guard fails, so Rust moves on to the next arm:
let age = Some(15);
match age {
Some(a) if a >= 18 => println!("adult"),
Some(a) => println!("minor, age {a}"),
None => println!("no age given"),
}A guard can see variables from outside
The pattern binds a fresh name n, and the guard compares it with the outer target. Writing Some(target) in the pattern binds a new target. It does not compare. The guard is the escape hatch for that trap:
let target = 10;
let value = Some(10);
match value {
Some(n) if n == target => println!("hit the target"),
_ => println!("missed"),
}One guard covers the whole or-pattern
In 4 | 5 | 6 if y, the guard belongs to all three choices, not just the 6. x is 5, so the pattern matches — but y is false, so the whole arm is skipped:
let x = 5;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}Example
fn main() {
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("The number {} is even", x),
Some(x) => println!("The number {} is odd", x),
None => println!("no number"),
}
let target = 10;
let value = Some(10);
match value {
Some(n) if n == target => println!("matched the target {}", target),
_ => println!("something else"),
}
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
}Key points
- Guards exist only in match: if let and while let have none — put the extra test inside the block instead
- When a guard is present, the compiler cannot check exhaustiveness for that arm.
- Keep a catch-all arm.
- The catch-all arm is the safety net.
- A guard can test whether a number is even, or whether it equals a setting stored elsewhere.
In this example, three guards do three jobs. First, Some(x) matches Some(4) and the guard x % 2 == 0 is true, so the even line prints. Second, the pattern binds a fresh name n. The guard compares it with the outer target, 10. They match. So matched the target 10 prints. Writing Some(target) instead would bind a new target. It would have matched anything. Third, x is 4. So the pattern 4 | 5 | 6 matches. But the guard if y is false. It is false for the whole pattern. So the arm is skipped. The catch-all prints no.
Note
A pattern alone cannot say "equal to that variable over there".
Naming a variable in a pattern creates a binding.
The binding is always a new binding.
The guard is the escape hatch built for this trap.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
In the arm 4 | 5 | 6 if y => ..., what does the guard if y apply to?