Спростовуваність: Чи Може Шаблон Бути Невідповідним
Шаблони бувають двох видів: спростовні та неспростовні. Шаблони, які збігаються з будь-яким можливим переданим значенням, є неспростовними. Прикладом може бути 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;
}
Якщо значення 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); } }
Ми дали коду вихід! Цей код абсолютно дійсний, хоча це означає, що ми не можемо використовувати неспростовний шаблон без отримання помилки. Якщо ми дамо if let
шаблон, який завжди збігатиметься, наприклад, x
, як показано у Блоці Коду 18-10, компілятор видасть попередження.
fn main() { if let x = 5 { println!("{}", x); }; }
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
.
Тепер, коли ви знаєте, де використовувати шаблони і в чому різниця між спростовуваними і не спростовуваними шаблонами, розгляньмо весь синтаксис, який можна використовувати для створення шаблонів.