mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
107 lines
4.7 KiB
Markdown
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) |