Explain pattern exhaustiveness checking
This commit is contained in:
parent
7f9c36b6a9
commit
ada57a32f2
|
|
@ -4,4 +4,84 @@ In Rust, pattern matching and bindings have a few very helpful properties. The
|
||||||
compiler will check that bindings are irrefutable when made and that match arms
|
compiler will check that bindings are irrefutable when made and that match arms
|
||||||
are exhaustive.
|
are exhaustive.
|
||||||
|
|
||||||
**TODO**: write this chapter.
|
## Pattern usefulness
|
||||||
|
|
||||||
|
The central question that usefulness checking answers is:
|
||||||
|
"in this match expression, is that branch reachable?".
|
||||||
|
More precisely, it boils down to computing whether,
|
||||||
|
given a list of patterns we have already seen,
|
||||||
|
a given new pattern might match any new value.
|
||||||
|
|
||||||
|
For example, in the following match expression,
|
||||||
|
we ask in turn whether each pattern might match something
|
||||||
|
that wasn't matched by the patterns above it.
|
||||||
|
Here we see the 4th pattern is redundant with the 1st;
|
||||||
|
that branch will get an "unreachable" warning.
|
||||||
|
The 3rd pattern may or may not be useful,
|
||||||
|
depending on whether `Foo` has other variants than `Bar`.
|
||||||
|
Finally, we can ask whether the whole match is exhaustive
|
||||||
|
by asking whether the wildcard pattern (`_`)
|
||||||
|
is useful relative to the list of all the patterns in that match.
|
||||||
|
Here we can see that `_` is useful (it would catch `(false, None)`);
|
||||||
|
this expression would therefore get a "non-exhaustive match" error.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// x: (bool, Option<Foo>)
|
||||||
|
match x {
|
||||||
|
(true, _) => {} // 1
|
||||||
|
(false, Some(Foo::Bar)) => {} // 2
|
||||||
|
(false, Some(_)) => {} // 3
|
||||||
|
(true, None) => {} // 4
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Thus usefulness is used for two purposes:
|
||||||
|
detecting unreachable code (which is useful to the user),
|
||||||
|
and ensuring that matches are exhaustive (which is important for soundness).
|
||||||
|
|
||||||
|
## Where it happens
|
||||||
|
|
||||||
|
This check is done to any expression that desugars to a match expression in MIR.
|
||||||
|
That includes actual `match` expressions,
|
||||||
|
but also anything that looks like pattern matching,
|
||||||
|
including `if let`, destructuring `let`, and similar expressions.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// `match`
|
||||||
|
// Usefulness can detect unreachable branches and forbid non-exhaustive matches.
|
||||||
|
match foo() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => panic!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// `if let`
|
||||||
|
// Usefulness can detect unreachable branches.
|
||||||
|
if let Some(x) = foo() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// `while let`
|
||||||
|
// Usefulness can detect infinite loops and dead loops.
|
||||||
|
while let Some(x) = it.next() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructuring `let`
|
||||||
|
// Usefulness can forbid non-exhaustive patterns.
|
||||||
|
let Foo::Bar(x, y) = foo();
|
||||||
|
|
||||||
|
// Destructuring function arguments
|
||||||
|
// Usefulness can forbid non-exhaustive patterns.
|
||||||
|
fn foo(Foo { x, y }: Foo) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## The algorithm
|
||||||
|
|
||||||
|
Exhaustiveness checking is implemented in [check_match].
|
||||||
|
The core of the algorithm is in [_match].
|
||||||
|
That file contains a detailed description of the algorithm.
|
||||||
|
|
||||||
|
[check_match]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/check_match/index.html
|
||||||
|
[_match]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/_match/index.html
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue