Background
The match operator in Rust is a powerful pattern matching tool borrowed from functional languages. Unlike equivalents in C/C++, Rust enforces strict exhaustiveness checking, and every possible variant must have a branch.
Problem
Errors when working with match are often related to incorrectly accounting for all type variants (for example, enums), improper handling of guards (conditions on branches), or complex nested structures. Incorrect handling leads to compile-time errors or implicitly wrong logic.
Solution
_).if after the pattern).Example code:
enum Shape { Circle(f64), Rectangle { width: f64, height: f64 }, } fn print_area(s: Shape) { match s { Shape::Circle(r) if r > 0.0 => println!("area = {}", 3.14 * r * r), Shape::Rectangle { width, height } if width > 0.0 && height > 0.0 => println!("area = {}", width * height), _ => println!("invalid shape"), } }
Key features:
Does the order of branches in match affect execution?
Yes, the order of branches is important: as soon as the first match is found, subsequent ones are not checked. This is especially critical with pattern guards — if there is an earlier branch with a guard, it will intercept the value before the catch-all.
Is catch-all (_) mandatory in match for enums?
No, if you have explicitly covered all cases (and the type declarations themselves will not change over time). However, catch-all is needed when working with types that may have additional values or when you do not want to explicitly handle all branches.
Can multiple patterns (alternatives) be used in a single match branch?
Yes. You can combine patterns using a vertical bar (|):
match x { 1 | 2 | 3 => println!("one, two or three"), _ => println!("something else"), }
_ too early (before specific branches)A developer wrote a match with a catch-all at the beginning and could not correctly handle specific cases.
Pros:
The program compiles.
Cons:
The specific logic never works, part of the code is not covered.
Explicit handling of all enum variants, catch-all only as the last branch, separate guards for atypical cases.
Pros:
Predictability, the compiler helps not to forget a variant, easy to extend types.
Cons:
Additional boilerplate code when there are many variants.