RustBrock/Refutability.md
darkicewolf50 730b0091e8
All checks were successful
Test Gitea Actions / first (push) Successful in 21s
Test Gitea Actions / check-code (push) Successful in 18s
Test Gitea Actions / test (push) Successful in 18s
Test Gitea Actions / documentation-check (push) Successful in 17s
finished ch19.2 and started ch19.3
2025-04-09 17:00:42 -06:00

107 lines
4.7 KiB
Markdown

# Refutability: Whether a Pattern Might Fail to Match
Patterns come in two forms:
- Refutable
- Irrefutable
Patterns that will match for any possible value passed are *irrefutable*.
An example would be `x` in the statement `let x = 5;` because `x` matches anything and therefore cannot fail to match.
Patterns that can fail to match for some possible value are *refutable*.
An example would be `Some(x)` in the expression `if let Some(x) = a_value` because if the value in the `a_value` variable is `None` rather than `Some`, the `Some(x)` pattern will not match.
Function parameters, `let` statements and `for` loops can only accept irrefutable patterns, because the program cannot do anything meaningful when values don't match.
The `if let` and `while let` expressions and the `let`-`else` statement accept refutable and irrefutable patterns, but the compiler warns against irrefutable patterns.
This is because by definition they are intended to handle possible failure.
The functionality of a conditional is in its ability to perform differently depending on success or failure.
Generally you shouldn't have to worry about the distinction between refutable and irrefutable patterns.
However you do need to be familiar with the concept of refutability so you can respond when you see it in an error message.
In these cases, you will need to change either the pattern or the construct you are using the pattern with, depending on the intended behavior of the code.
Now lets take a look at an example of what happens when you try to use a refutable pattern where Rust requires an irrefutable pattern and vice versa.
Here shows a `let` statement, but for the pattern we have specified `Some(x)`, a refutable pattern.
As expected, this code will not compile.
```rust
let Some(x) = some_option_value;
```
If `some_option_value` was a `None` value, it would fail to match the pattern `Some(x)`, meaning the pattern is refutable.
However, the `let` statement can only accept an irrefutable pattern because there is nothing valid the code can do with a `None` value.
At compile time, Rust will complain that we have tried to use a refutable pattern where an irrefutable pattern is required
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
--> src/main.rs:3:9
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
3 | let Some(x) = some_option_value else { todo!() };
| ++++++++++++++++
For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error
```
Because we didn't cover (and couldn't cover) every valid value with the pattern `Some(x)`, Rust rightfully produces a compiler error.
If we have a refutable pattern where an irrefutable pattern is needed, we can fix it by changing the code that uses the pattern.
Instead of using `let`, we can use `if let`.
Then if the code doesn't match, the code will just skip the code in the curly brackets, giving a way to continue validly.
Here shows a fix to the code
```rust
if let Some(x) = some_option_value {
println!("{x}");
}
```
We given the code an out, this code is perfectly valid now.
However, if we give `if let` an irrefutable pattern, such as `x`, the compiler will give a warning.
```rust
if let x = 5 {
println!("{x}");
};
```
Rust complains that it doesn't make sense to use `if let` with an irrefutable pattern
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: `patterns` (bin "patterns") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
```
For this reason, match arms must use refutable patterns, except for the last arm, which should match any remaining values with an irrefutable pattern.
Rust allows us to use an irrefutable pattern in a `match` with only one arm.
This syntax isn't particularly useful and could be replaced with a simpler `let` statement.
Next we will cover all the syntax we can use to create patterns [here](./Pattern%20Syntax.md)