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

Odrzucalność: Czy Wzorzec Może Nie Pasować

Wzorce występują w dwóch formach: odrzucalne i nieodrzucalne. Wzorce, które pasują do każdej możliwej przekazanej wartości, są nieodrzucalne. Przykładem może być x w instrukcji let x = 5;, ponieważ x pasuje do wszystkiego i dlatego nie może się nie dopasować. Wzorce, które mogą nie pasować do pewnej możliwej wartości, są odrzucalne. Przykładem może być Some(x) w wyrażeniu if let Some(x) = a_value, ponieważ jeśli wartość w zmiennej a_value to None, a nie Some, wzorzec Some(x) nie zostanie dopasowany.

Parametry funkcji, instrukcje let i pętle for mogą akceptować tylko wzorce nieodrzucalne, ponieważ program nie może nic znaczącego zrobić, gdy wartości nie pasują. Wyrażenia if let i while let oraz instrukcja let...else akceptują wzorce odrzucalne i nieodrzucalne, ale kompilator ostrzega przed wzorcami nieodrzucalnymi, ponieważ z definicji są przeznaczone do obsługi możliwych błędów: funkcjonalność warunku polega na jego zdolności do działania inaczej w zależności od sukcesu lub porażki.

Ogólnie rzecz biorąc, nie powinieneś martwić się rozróżnieniem między wzorcami odrzucalnymi i nieodrzucalnymi; jednak musisz być zaznajomiony z koncepcją odrzucalności, aby móc reagować, gdy zobaczysz ją w komunikacie o błędzie. W takich przypadkach będziesz musiał zmienić albo wzorzec, albo konstrukcję, z którą używasz wzorca, w zależności od zamierzonego zachowania kodu.

Przyjrzyjmy się przykładowi, co się dzieje, gdy próbujemy użyć wzorca odrzucalnego tam, gdzie Rust wymaga wzorca nieodrzucalnego, i odwrotnie. Lista 19-8 pokazuje instrukcję let, ale dla wzorca określiliśmy Some(x), wzorzec odrzucalny. Jak można się spodziewać, ten kod się nie skompiluje.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}
Lista 19-8: Próba użycia wzorca odrzucalnego z let

Gdyby some_option_value miało wartość None, nie pasowałoby do wzorca Some(x), co oznacza, że wzorzec jest odrzucalny. Jednak instrukcja let może przyjmować tylko wzorzec nieodrzucalny, ponieważ nie ma niczego ważnego, co kod mógłby zrobić z wartością None. W czasie kompilacji Rust skarży się, że próbowaliśmy użyć wzorca odrzucalnego tam, gdzie wymagany jest wzorzec nieodrzucalny:

$ 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

Ponieważ nie pokryliśmy (i nie mogliśmy pokryć!) każdej prawidłowej wartości wzorcem Some(x), Rust słusznie zwraca błąd kompilacji.

Jeśli mamy wzorzec odrzucalny tam, gdzie potrzebny jest wzorzec nieodrzucalny, możemy to naprawić, zmieniając kod, który używa wzorca: zamiast używać let, możemy użyć let...else. Wtedy, jeśli wzorzec nie pasuje, kod w nawiasach klamrowych obsłuży wartość. Lista 19-9 pokazuje, jak naprawić kod z Listy 19-8.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}
Lista 19-9: Użycie let...else i bloku z wzorcami odrzucalnymi zamiast let

Daliśmy kodowi wyjście! Ten kod jest całkowicie poprawny, chociaż oznacza to, że nie możemy użyć wzorca nieodrzucalnego bez otrzymywania ostrzeżenia. Jeśli damy let...else wzorzec, który zawsze będzie pasował, taki jak x, jak pokazano na Liście 19-10, kompilator wyświetli ostrzeżenie.

fn main() {
    let x = 5 else {
        return;
    };
}
Lista 19-10: Próba użycia wzorca nieodrzucalnego z let...else

Rust skarży się, że użycie let...else z wzorcem nieodrzucalnym nie ma sensu:

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

Z tego powodu ramiona match muszą używać wzorców odrzucalnych, z wyjątkiem ostatniego ramienia, które powinno pasować do wszystkich pozostałych wartości za pomocą wzorca nieodrzucalnego. Rust pozwala nam używać wzorca nieodrzucalnego w match z tylko jednym ramieniem, ale ta składnia nie jest szczególnie użyteczna i mogłaby być zastąpiona prostszą instrukcją let.

Teraz, gdy wiesz, gdzie używać wzorców i jaka jest różnica między wzorcami odrzucalnymi i nieodrzucalnymi, przejdźmy do omówienia całej składni, której możemy użyć do tworzenia wzorców.