Common Pattern-Matching Errors
Most pattern-matching mistakes in Rust fall into four categories: accidentally shadowing a variable instead of comparing its value, moving a non-Copy field out of a borrowed reference, ordering general patterns before specific patterns, and simultaneously binding a whole value while moving one of its fields.
Summary of Common Pattern-Matching Pitfalls
| Pitfall | Compiler Warning or Error | Root Cause | Fix |
|---|---|---|---|
Bare identifier in match arm (expected_val => ...) |
Warning: unreachable_patterns + unused_variables |
A bare lowercase identifier in a pattern is a new variable binding that matches anything, not an equality check against an outer variable | Use a const (EXPECTED_VAL) or a match guard (x if x == expected_val) |
Moving a non-Copy field out of &Enum or &Struct |
E0507: cannot move out of ... which is behind a shared reference |
Explicit *deref or by-value move of a String/Vec behind & |
Match on &scrutinee directly so match ergonomics binds &String, or clone |
Binding whole @ Variant(inner) when inner moves |
E0382: use of partially moved value |
Moving inner out of Variant invalidates whole |
Match by reference (&scrutinee) or bind ref inner |
| Placing a general pattern before a specific pattern | Warning: unreachable pattern |
match evaluates arms top-to-bottom |
Order specific arms before general or catch-all arms |
Error 1: Accidental Variable Shadowing in a match Arm
A bare variable name in a pattern always creates a new binding that matches every value and shadows any outer variable of the same name:
fn check_pin(input: u16) {
let secret_pin = 1234;
match input {
// BUG: `secret_pin` here is a brand-new pattern variable that matches ANY `u16`!
// It does NOT compare `input == 1234`!
x if x == secret_pin => println!("PIN matched ({x})!"),
other => println!("Wrong PIN: {other}"),
}
}
fn main() {
check_pin(9999);
check_pin(1234);
}
Wrong PIN: 9999
PIN matched (1234)!
Always use either a const item (const SECRET_PIN: u16 = 1234; can be used directly as a pattern!) or a match guard (x if x == secret_pin) when comparing against a runtime variable.
Error 2: Moving Out of a Shared Reference in a Pattern (E0507)
If you dereference a shared reference in the match scrutinee (match *opt_ref) when the inner type is not Copy, attempting to bind the payload by value fails with E0507:
// ❌ does not compile
fn print_opt(opt: &Option<String>) {
match *opt {
Some(text) => println!("{text}"),
None => println!("empty"),
}
}
fn main() {}
error[E0507]: cannot move out of `*opt` which is behind a shared reference
--> src/main.rs:2:11
|
2 | match *opt {
| ^^^^
3 | Some(text) => println!("{text}"),
| ----
| |
| data moved here
| move occurs because `text` has type `String`, which does not implement the `Copy` trait
|
help: consider removing the dereference here
|
2 - match *opt {
2 + match opt {
|
Fix: Match on opt Directly Without *
Matching on opt (&Option<String>) activates match ergonomics and binds text as &String without moving.
Error 3: Simultaneously Binding whole @ and Moving an Inner Field (E0382)
When matching an owned non-Copy value by value, whole @ Wrapper(inner) attempts to move the entire value into whole while also moving its inner field into inner:
// ❌ does not compile
#[derive(Debug)]
struct Wrapper(String);
fn main() {
let w = Wrapper(String::from("data"));
match w {
whole @ Wrapper(inner) => println!("{whole:?}, {inner}"),
}
}
error[E0382]: borrow of moved value: `w`
--> src/main.rs:7:9
|
7 | whole @ Wrapper(inner) => println!("{whole:?}, {inner}"),
| ^^^^^^^^^^^^^^^^-----^
| | |
| | value moved here
| value borrowed here after move
|
= note: move occurs because `w.0` has type `String`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
|
7 | ref whole @ Wrapper(ref inner) => println!("{whole:?}, {inner}"),
| +++ +++
Fix: Match &w by Reference (or Use ref)
#[derive(Debug)]
struct Wrapper(String);
fn main() {
let w = Wrapper(String::from("data"));
match &w {
whole @ Wrapper(inner) => println!("{whole:?}, {inner}"),
}
}
Wrapper("data"), data