Refutability: Whether a Pattern Might Fail to Match
Шаблоны бывают двух типов: refutable и irrefutable. Шаблоны, которые будут совпадать для любого возможного переданного значения, являются irrefutable. Например, x
в выражении let x = 5;
, потому что x
совпадает с любым значением и, следовательно, не может не совпадать. Шаблоны, которые могут не совпадать для некоторых возможных значений, являются refutable. Например, Some(x)
в выражении if let Some(x) = a_value
, потому что если значение в переменной a_value
равно None
вместо Some
, шаблон Some(x)
не совпадет.
Параметры функций, let
-операторы и циклы for
могут принимать только irrefutable шаблоны, потому что программа не может сделать ничего полезного, когда значения не совпадают. Выражения if let
и while let
могут принимать refutable и irrefutable шаблоны, но компилятор предупреждает о использовании irrefutable шаблонов, потому что по определению они предназначены для обработки возможной ошибки: функциональность условного оператора заключается в том, чтобы выполняться по-разному в зависимости от успеха или неудачи.
В общем, вам не нужно беспокоиться о различии между refutable и irrefutable шаблонами; однако, вам нужно быть знакомым с концепцией refutability, чтобы знать, как реагировать, когда вы видите ее в сообщении об ошибке. В таких случаях вам нужно изменить либо шаблон, либо конструкцию, в которой вы используете шаблон, в зависимости от предполагаемого поведения кода.
Рассмотрим пример того, что происходит, когда мы пытаемся использовать refutable шаблон, где Rust требует irrefutable шаблона, и наоборот. Listing 18-8 показывает let
-оператор, но для шаблона мы указали Some(x)
, refutable шаблон. Как вы могли ожидать, этот код не скомпилируется.
let Some(x) = some_option_value;
Listing 18-8: Попытка использовать refutable шаблон с let
Если some_option_value
было бы значением None
, оно не совпадало бы с шаблоном Some(x)
, что означает, что шаблон refutable. Однако, let
-оператор может принимать только irrefutable шаблон, потому что код не может сделать ничего полезного с значением None
. При компиляции Rust сообщит, что мы пытаемся использовать refutable шаблон, где требуется irrefutable шаблон:
error[E0005]: refutable pattern in local binding: `None` not covered
--> 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 `if let` to ignore the variant that isn't matched
|
3 | let x = if let Some(x) = some_option_value { x } else { todo!() };
| ++++++++++ ++++++++++++++++++++++
Поскольку мы не охватили (и не могли охватить!) все допустимые значения шаблоном Some(x)
, Rust правильно генерирует ошибку компиляции.
Если у нас есть refutable шаблон, где требуется irrefutable шаблон, мы можем исправить это, изменив код, который использует шаблон: вместо let
мы можем использовать if let
. Затем, если шаблон не совпадает, код просто пропустит код в фигурных скобках, обеспечив тем самым способность продолжить выполнение валидно. Listing 18-9 показывает, как исправить код из Listing 18-8.
if let Some(x) = some_option_value {
println!("{x}");
}
Listing 18-9: Использование if let
и блока с refutable шаблонами вместо let
Мы дали код выход! Этот код полностью валиден, хотя это означает, что мы не можем использовать irrefutable шаблон без получения ошибки. Если мы передадим if let
шаблон, который всегда совпадает, например, x
, как показано в Listing 18-10, компилятор выдаст предупреждение.
if let x = 5 {
println!("{x}");
};
Listing 18-10: Попытка использовать irrefutable шаблон с if let
Rust сообщает, что использовать if let
с irrefutable шаблоном не имеет смысла:
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
= note: this pattern will always match, so the `if let` is
useless
= help: consider replacing the `if let` with a `let`
По этой причине ветви match
должны использовать refutable шаблоны, за исключением последней ветви, которая должна совпадать с любыми оставшимися значениями с помощью irrefutable шаблона. Rust позволяет использовать irrefutable шаблон в match
с только одной ветвью, но этот синтаксис не особо полезен и может быть заменен на более простой let
-оператор.
Теперь, когда вы знаете, где использовать шаблоны и разницу между refutable и irrefutable шаблонами, давайте рассмотрим весь синтаксис, который мы можем использовать для создания шаблонов.