Спростовуваність: Чи Може Шаблон Бути Невідповідним

Шаблони бувають двох видів: спростовні та неспростовні. Шаблони, які збігаються з будь-яким можливим переданим значенням, є неспростовними. Прикладом може бути 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 допускають спростовні і неспростовні шаблони, але компілятор застерігає від неспростовних шаблонів, оскільки за визначенням вони призначені для обробки можливих збоїв: функціональність умови полягає в її здатності працювати по-різному в залежності від успіху або невдачі.

В цілому, ви не повинні турбуватися про різницю між спростовуваними і не спростовуваними шаблонами; однак, ви повинні бути знайомі з концепцією спростовуваності, щоб мати можливість реагувати, коли ви бачите її в повідомленні про помилку. У таких випадках вам потрібно буде змінити або шаблон, або конструкцію, в якій ви використовуєте шаблон, в залежності від бажаної поведінки коду.

Розгляньмо приклад того, що відбувається, коли ми намагаємося використати спростовуваний шаблон там, де Rust вимагає неспростовний шаблон і навпаки. У Блоці Коду 18-8 показано інструкцію let, але для шаблону ми вказали Some(x), спростовуваний шаблон. Як і слід було очікувати, цей код не буде компілюватися.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}

Listing 18-8: Attempting to use a refutable pattern with 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: `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: `Option<i32>` defined here
    = 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!() };
    |     ++++++++++                                 ++++++++++++++++++++++

For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` due to previous error

Оскільки ми не покрили (і не могли покрити!) кожне допустиме значення шаблоном Some(x), Rust справедливо видасть помилку компілятора.

Якщо у нас є спростовний шаблон там, де потрібен неспростовний, ми можемо виправити це, змінивши код, який використовує шаблон: замість використання let,, ми можемо використати if let. Тоді, якщо шаблон не збігається, код просто пропустить код у фігурних дужках, даючи йому можливість продовжити правильне виконання коду. У Блоці Коду 18-9 показано, як виправити код у Блоці Коду 18-8.

fn main() {
    let some_option_value: Option<i32> = None;
    if let Some(x) = some_option_value {
        println!("{}", x);
    }
}

Listing 18-9: Using if let and a block with refutable patterns instead of let

Ми дали коду вихід! Цей код абсолютно дійсний, хоча це означає, що ми не можемо використовувати неспростовний шаблон без отримання помилки. Якщо ми дамо if let шаблон, який завжди збігатиметься, наприклад, x, як показано у Блоці Коду 18-10, компілятор видасть попередження.

fn main() {
    if let x = 5 {
        println!("{}", x);
    };
}

Listing 18-10: Attempting to use an irrefutable pattern with if let

Rust скаржиться, що немає сенсу використовувати if let з неспростовним шаблоном:

$ 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: `#[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`

warning: `patterns` (bin "patterns") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/patterns`
5

З цієї причини рукави збігів повинні використовувати спростовувані шаблони, за винятком останнього плеча, яке повинно збігатися з будь-якими значеннями, що залишилися, з неспростовним шаблоном. Rust дозволяє використовувати неспростовний шаблон у match лише з одним рукавом, але цей синтаксис не є особливо корисним і може бути замінений простішим оператором let.

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