Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ОПРОВЕРЖИМОСТЬ: МОЖЕТ ЛИ ОБРАЗЕЦ НЕ СОВПАСТИ

Образцы бывают двух видов: опровержимые и неопровержимые. Образцы, которые совпадают с любым возможным значением, являются неопровержимыми. Примером служит x в выражении let x = 5;, поскольку x совпадает с чем угодно и поэтому не может не совпасть. Образцы, которые могут не совпасть для некоторых значений, являются опровержимыми. Вот несколько примеров:

  • В выражении if let Some(x) = a_value образец Some(x) является опровержимым. Если значение в переменной a_value равно None, а не Some, образец Some(x) не совпадёт.
  • В выражении if let &[x, ..] = a_slice образец &[x, ..] является опровержимым. Если значение в переменной a_slice имеет нулевое количество элементов, образец &[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;
}
Listing 19-8: Попытка использовать опровержимый образец с 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 мы можем использовать if let. Тогда, если образец не совпадёт, код просто пропустит блок в фигурных скобках, что даст ему возможность продолжить корректно. Листинг 19-9 показывает, как исправить код из листинга 19-8.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}
Listing 19-9: Использование let...else и блока с опровержимыми образцами вместо let

Мы дали коду выход! Этот код теперь совершенно корректен. Однако, если мы дадим if let неопровержимый образец (образец, который всегда совпадёт), такой как x, как показано в листинге 19-10, компилятор выдаст предупреждение.

fn main() {
    let x = 5 else {
        return;
    };
}
Listing 19-10: Попытка использовать неопровержимый образец с if let

Rust жалуется, что использование if let с неопровержимым образцом бессмысленно:

$ 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.

Теперь, когда вы знаете, где использовать образцы и в чём разница между опровержимыми и неопровержимыми образцами, давайте рассмотрим весь синтаксис, который можно использовать для создания образцов.