Patterns
Rust patterns are a little like regular expressions for all your data. They’re used to test whether or not a value has a particular desired shape. They can extract several fields from a struct or tuple into local variables all at once. And like regular expressions, they are concise, typically doing it all in a single line of code.
Pattern matching an enum, struct, or tuple works as though Rust is doing a simple left-to-right scan, checking each component of the pattern to see if the value matches it.
Rust’s patterns are a mini-language of their own. A pattern can match a range of values. It can unpack tuples. It can match against individual fields of structs. It can chase references, borrow parts of a value, and more.
Patterns and expressions are natural opposites. Expressions produce values; patterns consume values. The two use a lot of the same syntax. The expression (x, y)
makes two values into a new tuple, but the pattern (x, y)
does the opposite: it matches a tuple and breaks out the two values. It’s the same with &
. In an expression, &
creates a reference. In a pattern, &
matches a reference.
match
Expressions
Patterns are most prominent in match
expressions. The general form of a match
expression is:
|
|
- The comma after an arm may be dropped if the
expr
is a block.
Patterns are the parts that appear before the =>
symbol. The versatility of match
stems from the variety of supported patterns that can be used to the left of =>
in each arm.
All arms of a match
expression must have the same type.
In Rust, if
and match
can produce values.
Propagating Errors
Ordinary errors are handled using the Result
type. Result
is an enum.
|
|
The most thorough way of dealing with a Result
is using a match
expression.
In most places where we try something that could fail, we don’t want to catch and handle the error immediately. Instead, if an error occurs, we usually want to let our caller deal with it. We want errors to propagate up the call stack.
|
|
This kind of match
statement is such a common pattern in Rust that the language provides the ?
operator as shorthand for the whole thing. You can add a ?
to any expression that produces a Result
, such as the result of a function call:
|
|
?
also works similarly with the Option
type. In a function that returns Option
, you can use ?
to unwrap a value and return early in the case of None
:
|
|
if let
|
|
An if let
expression is shorthand for a match
with just one pattern:
|
|
Loops
|
|
Patterns in Other Places
Patterns are also allowed in several other places, typically in place of an identifier. The meaning is always the same: instead of just storing a value in a single variable, Rust uses pattern matching to take the value apart.
|
|
Each of these saves two or three lines of boilerplate code. The same concept exists in some other languages: in JavaScript, it’s called destructuring, while in Python, it’s unpacking.
In all four examples, we use patterns that are guaranteed to match. The pattern Point3d { x, y, z }
matches every possible value of the Point3d
struct type, (x, y)
matches any (f64, f64)
pair, and so on. Patterns that always match are special in Rust. They’re called irrefutable patterns, and they’re the only patterns allowed in the four places shown here (after let
, in function arguments, after for
, and in closure arguments).
A refutable pattern is one that might not match, like Ok(x)
, which doesn’t match an error result, or '0' ..= '9'
, which doesn’t match the character 'Q'
. Refutable patterns can be used in match
arms, because match
is designed for them: if one pattern fails to match, it’s clear what happens next.
The four preceding examples are places in Rust programs where a pattern can be handy, but the language doesn’t allow for match failure.