Pattern matching

Pattern matching has two purposes in Rust: To check if a value has certain characteristics, and to bind parts of the value to variables.

ExampleEdit

Patterns are used in many places. For example, they appear on the left side of match arms.

#[derive(Debug)]
struct Foo {
    bar: i32,
    baz: bool,
}

let value = Some(Foo { bar: 42, baz: true });

match value {
    Some(Foo { bar: 42, baz: false }) => println!("first match arm"),
    Some(Foo { bar: 42, .. })         => println!("second match arm"),
    Some(_)                           => println!("third match arm"),
    None                              => println!("last match arm"),
}
// This prints "second match arm"

When this example is run, the match arms are matched against the value from top to bottom, and the first arm that matches the value is executed.

Pattern matching in a match expression must always be exhaustive. If the third or last match arm was removed, this example would no longer be accepted by the compiler, because every possible value has to be covered by at least one pattern.

BindingsEdit

When pattern matching, parts of the value can be bound to new variables. These can be made mutable by prepending the mut keyword:

match value {
    x @ Some(Foo { baz: false, .. }) => println!("x is {:?}", x),
    Some(Foo { bar: mut x, .. })     => println!("x is {:?}", x),
    None                             => println!("value is None"),
}

In this example, if the first arm matches the value, the whole value is bound to the variable x. In the second arm, only the field bar is bound to x.

Note that x is a different binding in each match arm. They have different types, and only the binding in the second match arm is mutable.

RefutabilityEdit

Patterns can either be irrefutable, meaning that matching the pattern can never fail; otherwise they are refutable. For example, the wildcard pattern (_) and variable bindings are irrefutable, as matching them will always succeed.

Irrefutable patterns are used in let bindings and in function and closure parameters:

fn pattern_matching((a, b): (i32, &bool)) {
    let &c = b;
}

In this example, (a, b) and &c are patterns. a is an i32, b is a &bool, c is a bool.

The process of taking a value apart with pattern matching and binding its components to new variables is called destructuring.

PatternsEdit

There are many ways how values can be matched:

  • Wildcard (_): Matches any value and ignores it
  • Binding (e.g. foo): Matches any value and binds it to a new variable
  • Binding with additional pattern (e.g. foo @ Some(_)): Matches a value against the pattern after the @. If it succeeds, the whole value is bound to the variable.
    • Currently, the pattern after @ can't introduce new bindings. This restriction will be lifted when the bindings_after_at nightly-only feature is stabilized.
  • Literal (e.g. 5, true, 'a', ""): Matches exactly that literal
  • Range (e.g. 5..10, 'a'..'f'): Matches any value in the given range. This looks identical to but is not that same as the range operator.
    • Ranges with .. are exclusive — they don't include the upper bound
    • Ranges with ..= are inclusive. It's also possible to write ..., but this syntax is deprecated and produces a warning.
    • Unlike in expressions, half-open and unbounded ranges are not allowed in patterns.
  • Constant (e.g. crate::FOO): Matches a constant.
  • Reference (&PATTERN): Matches the dereferenced value
  • Tuple (e.g. (PATTERN, .., PATTERN)): Destructures a tuple of values
    • May contain two dots (..) to ignore an arbitrary number of components
  • Array/slice (e.g. [PATTERN, .., PATTERN])
    • May contain two dots (..) to ignore an arbitrary number of elements. These can be bound as an array/slice to a new variable with binding @ ..
  • Struct (e.g. Foo { field: PATTERN, .. }, Foo(PATTERN, ..))
    • May contain two dots (..) to ignore an arbitrary number of struct fields
    • If a field is bound to a variable of the same name, the pattern can be omitted. For example, Foo { field: field } is equivalent to Foo { field }
    • A tuple struct can be pattern-matched with the regular struct syntax, e.g. let Foo { 0: x } = Foo(5);
  • Enum variant (e.g. Option::Some(PATTERN))
    • Enum variants are always refutable, except if the enum contains at most one variant and doesn't have the #[non_exhaustive] attribute
    • To the enum variant fields apply the same pattern-matching rules as to struct fields
  • ref (e.g. Some(ref x)): This syntax is only required in specific scenarios thanks to match ergonomics. A pattern after ref only borrows the matched value, instead of taking ownership.
  • box (e.g. Some(box x)): This is a nightly feature to destructure a Box . It's discouraged to use this feature, since it might be removed in the future.

Match ergonomicsEdit

Match ergonomics make it easier to match on borrowed values by automatically adding the appropriate symbols where necessary. Imagine you are writing a function to unwrap a struct from a borrowed Result , which doesn't implement the Copy  trait:

struct Foo(bool, i32);

fn unwrap(f: &Result<Foo, ()>) -> &Foo {
    match f { ... }
}

Without match ergonomics, we have to use & to match on the reference, and then use the ref keyword to bind the content by reference:

match f {
    &Ok(ref foo) => foo,
    &Err(()) => panic!("Unwrapped an error"),
}

However, with match ergonomics, this isn't necessary, because &Ok(foo) is treated the same as Ok(&foo) or Ok(Foo(&a, &b)) in patterns; the reference can be moved inside structs or enums:

fn unwrap(f: &Result<Foo, ()>) -> &Foo {
    match f {
        Ok(foo) => foo,
        Err(()) => panic!("Unwrapped an error"),
    }
}

This also works for tuples, arrays, slices and unions.

Places where patterns are usedEdit

Refutable patternsEdit

match blockEdit

Full article: Match

With the match  keyword, a value is pattern matched against several match arms. Each match arm consists of at least one pattern and an optional if guard. Patterns in a match block must be exhaustive.

if let and while letEdit

These blocks are syntactic sugar for the often more verbose match block. For example:

if let Some(x) = args().next() {
    dbg!(x);
}
// desugars to
match args().next() {
    Some(x) => {
        dbg!(x);
    }
    _ => {}
}
let mut iter = "abc".chars();
while let Some(x) = iter.next() {
    dbg!(x);
}
// desugars to
let mut iter = "abc".chars();
loop {
    match iter.next() {
        Some(x) => {
            dbg!(x);
        }
        None => break,
    }
}

Like match blocks, if let and while let blocks can have multiple patterns separated with vertical bars (|). However, they can't have an if guard.

Some people find the naming if let and while let confusing. The rationale is that both let and if let accept a pattern. However, the pattern must be irrefutable for let, but can be refutable for if let.

Irrefutable patternsEdit

let bindingsEdit

let bindings are patterns. This is why it's possible to destructure values on assignment:

let [x, ..] = [1, 2, 3];
let &(a, b) = &(3, 2);

for loopsEdit

for loops accept a pattern to bind the iterated-over values, for example:

for (n, &x) in vec![1, 2, 3].iter().enumerate() {
    dbg!(n, x);
}

Function and closure parametersEdit

Function and closure parameters are patterns, for example:

fn foo((a, b): (i32, i32), _: bool) {}

[1, 2, 3]
    .iter()
    .zip(&[4, 5, 6])
    .map(|(&n, &x)| n + x);

MacrosEdit

Macros using macro_rules! can accept patterns with a pat parameter, for example:

macro_rules! matches {
    // accepts an expression and a pattern
    ($e:expr, $p:pat) => {
        match $e {
            $p => true,
            _  => false,
        }
    }
}

In fact, a similar macro, matches! , is part of the standard library.

Future extensionsEdit

There are several upcoming features that will make pattern matching simpler and more ergonomic. Some of them were discussed in an Inside-Rust blop post.

Nested OR-patternsEdit

match blocks support several patterns separated with vertical bars (|). However, they can't be nested, for example, Some(1 | 3) is illegal as a pattern.

Patterns like this will be allowed when the experimental or_patterns feature is stabilized. This will also allow irrefutable OR-patterns in let bindings and function/closure parameters, for example:

#![feature(or_patterns)]

let Ok(x) | Err(x) = Ok(5);

Bindings after @Edit

As mentioned above, patterns after a @ can't introduce new bindings. For example, foo @ Some(bar) is illegal.

This restriction will be lifted when the experimental bindings_after_at feature is stabilized. Note that the matched type must implement the Copy  trait:

#![feature(bindings_after_at)]

match Some(5) {
    x @ Some(y) => {
        dbg!(x, y);
    }
    _ => {}
}