Опровержимость: может ли шаблон не совпасть
Шаблоны бывают двух форм: опровержимые и неопровержимые. Шаблоны, которые
совпадут с любым возможным переданным значением, являются неопровержимыми.
Примером может быть x в операторе let x = 5;, потому что x соответствует
чему угодно и поэтому не может не совпасть. Шаблоны, которые могут не совпасть
с некоторым возможным значением, являются опровержимыми. Примером может быть
Some(x) в выражении if let Some(x) = a_value, потому что если значение в
переменной a_value будет None, а не Some, шаблон Some(x) не совпадет.
Параметры функций, операторы let и циклы for могут принимать только
неопровержимые шаблоны, потому что программа не может сделать ничего
осмысленного, когда значения не совпадают. Выражения if let и while let, а
также оператор let...else принимают и опровержимые, и неопровержимые
шаблоны, но компилятор предупреждает о неопровержимых шаблонах, потому что по
определению эти конструкции предназначены для обработки возможной неудачи:
функциональность условной конструкции состоит в ее способности вести себя
по-разному в зависимости от успеха или неудачи.
В целом вам не придется часто беспокоиться о различии между опровержимыми и неопровержимыми шаблонами; однако нужно быть знакомым с концепцией опровержимости, чтобы понимать, как реагировать, когда она встречается в сообщении об ошибке. В таких случаях вам нужно будет изменить либо шаблон, либо конструкцию, с которой вы используете шаблон, в зависимости от желаемого поведения кода.
Давайте посмотрим, что происходит, когда мы пытаемся использовать опровержимый
шаблон там, где Rust требует неопровержимый шаблон, и наоборот. Листинг 19-8
показывает оператор let, но в качестве шаблона мы указали Some(x),
опровержимый шаблон. Как можно ожидать, этот код не скомпилируется.
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
letЕсли бы some_option_value было значением None, оно не совпало бы с
шаблоном Some(x), а значит, шаблон является опровержимым. Однако оператор
let может принимать только неопровержимый шаблон, потому что с значением
None код не может сделать ничего допустимого. Во время компиляции Rust
пожалуется, что мы попытались использовать опровержимый шаблон там, где
требуется неопровержимый:
$ 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/ch19-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
Поскольку мы не покрыли (и не могли покрыть!) каждое допустимое значение
шаблоном Some(x), Rust справедливо выдает ошибку компилятора.
Если у нас есть опровержимый шаблон там, где нужен неопровержимый, мы можем
исправить это, изменив код, использующий шаблон: вместо let можно
использовать let...else. Тогда, если шаблон не совпадет, код в фигурных
скобках обработает значение. Листинг 19-9 показывает, как исправить код из
листинга 19-8.
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value else {
return;
};
}
let...else и блока с опровержимыми шаблонами вместо letМы дали коду выход! Этот код полностью допустим, хотя это означает, что мы не
можем использовать неопровержимый шаблон без предупреждения. Если мы передадим
let...else шаблон, который всегда будет совпадать, например x, как
показано в листинге 19-10, компилятор выдаст предупреждение.
fn main() {
let x = 5 else {
return;
};
}
let...elseRust сообщает, что использовать let...else с неопровержимым шаблоном не
имеет смысла:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `let...else` pattern
--> src/main.rs:2:5
|
2 | let x = 5 else {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `else` clause is useless
= help: consider removing the `else` clause
= 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`
По этой причине ветви match должны использовать опровержимые шаблоны, за
исключением последней ветви, которая должна сопоставлять все оставшиеся
значения с неопровержимым шаблоном. Rust позволяет использовать
неопровержимый шаблон в match всего с одной ветвью, но такой синтаксис не
особенно полезен и может быть заменен более простым оператором let.
Теперь, когда вы знаете, где использовать шаблоны, и понимаете разницу между опровержимыми и неопровержимыми шаблонами, давайте рассмотрим весь синтаксис, который можно использовать для создания шаблонов.