Exhaustive Matching

This chapter covers a small, specific thing about match statements. It warrants its own chapter anyways because it can have a big effect of the correctness of code using these statements. Now what does 'exhaustive matching' mean? It means that every possible value that can put inside the statement must at least match one arm. The reason is that programmers sometimes tend to forget cases, leading to errors. With the extensive pattern matching of Rust, this problem would be magnified compared to other languages.

If you want to match all cases that don't need special treatment, you can use a irrefutable pattern for default handling. If it doesn't need any handling at all, you can use the default pattern _, which essentially is an irrefutable pattern that matches the entire value and doesn't bind anything. But even if your default arm is empty, if you don't match all cases, it needs to exist. It's still a signal to the programmer that some variants exist that don't need handling there.

It can also be a convenience feature. Imagine having some enum and matching it in different places. Now if you don't use default matching, adding another variant to an enum will throw you one error for each match statement using it. As the error messages tell you where exactly it is, you can use them to find every single match statement and add the new variant to it. This can greatly reduce the amount or errors you produce in this case.

Although I don't think there's a great need to provide an example here, I would feel bad writing such a short chapter without any code, so here you go:


#![allow(unused)]
fn main() {
let value: usize = 42;

// Missing 10..99!
// Will not compile
match value {
  0..=10 => {}
  99.. => {}
}
  
// Default handler, compiles
match value {
  1 => {}
  2 => {}
  3 => {}
  rest => {}
}
}