Refutability: Whether a Pattern Might Fail to Match

Patterns come in two forms: refutable and irrefutable. Patterns that will match for any possible value passed are said to be irrefutable. An example would be x in the statement let x = 5; because x matches anything and so cannot fail to match. Patterns that may fail to match for some possible value are said to be refutable. An example of this would be Some(x) in the expression if let Some(x) = a_value; if the value in the a_value variable is None rather than Some, then the Some(x) pattern would not match.

let statements, function parameters, and for loops can only accept irrefutable patterns, because the program cannot do anything meaningful with values that don’t match. The if let and while let expressions are restricted to only accept refutable patterns, because by definition they’re intended to handle possible failure---the functionality of a conditional is in its ability to perform differently upon success and failure.

In general, you shouldn’t have to worry about the distinction between refutable and irrefutable patterns, but you do need to be familiar with the concept of refutability so you can respond when you see it in an error message. In those cases, you’ll need to change either the pattern or the construct you’re using the pattern with, depending on your intentions for the behavior of the code.

Let’s look at an example of what happens if we try to use a refutable pattern where Rust requires an irrefutable pattern and vice versa. In Listing 18-8, we have a let statement, but for the pattern we’ve specified Some(x), a refutable pattern. As you might expect, this will error:

let Some(x) = some_option_value;

Listing 18-8: Attempting to use a refutable pattern with let

If some_option_value was a None value, it would fail to match the pattern Some(x), meaning the pattern is refutable. The let statement, however, can only accept an irrefutable patterns because there’s nothing valid the code could do with a None value. At compile time, Rust will complain that we’ve tried to use a refutable pattern where an irrefutable pattern is required:

error[E0005]: refutable pattern in local binding: `None` not covered
 --> <anon>:3:5
3 | let Some(x) = some_option_value;
  |     ^^^^^^^ pattern `None` not covered

We didn’t cover (and couldn’t cover!) every valid value with the pattern Some(x), so Rust will rightfully complain.

To fix the case where we have a refutable pattern in a place where an irrefutable pattern is needed, we can change the code that uses the pattern: instead of using let, we can use if let. That way, if the pattern doesn’t match, the code will just skip the code in the curly brackets, giving it a way to continue validly. Listing 18-9 shows how to fix the code in Listing 18-8.

# #![allow(unused_variables)]
#fn main() {
# let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
    println!("{}", x);

Listing 18-9: Using if let and a block with refutable patterns instead of let

We’ve given the code an out! This code is perfectly valid, though does now of course mean we cannot use an irrefutable pattern without receiving an error. If we give if let a pattern that will always match, such as x as shown in Listing 18-10, it will error:

if let x = 5 {
    println!("{}", x);

Listing 18-10: Attempting to use an irrefutable pattern with if let

Rust complains that it doesn’t make sense to use if let with an irrefutable pattern:

error[E0162]: irrefutable if-let pattern
 --> <anon>:2:8
2 | if let x = 5 {
  |        ^ irrefutable pattern

For this reason, match arms must use refutable patterns, except for the last arm that should match any remaining values with an irrefutable pattern. Using an irrefutable pattern in a match with only one arm is allowed, but isn’t particularly useful and could be replaced with a simpler let statement.

Now that we’ve discussed where patterns can be used and the difference between refutable and irrefutable patterns, let’s go over all the syntax we can use to create patterns.