SE VUOI PRENDERE LA CERTIFICAZIONE PER QUESTO CORSO CLICCA QUI
Refutabilità: Quando un Pattern Potrebbe Non Fare Match
I pattern si presentano in due forme: refutabili e irrefutabili. I pattern che faranno match per qualsiasi valore possibile passato sono irrefutabili. Un esempio sarebbe x nella dichiarazione let x = 5; perché x fa match con qualsiasi cosa e quindi non può fallire nel fare match. I pattern che possono fallire nel fare match per alcuni valori possibili sono refutabili. Un esempio sarebbe Some(x) nell’espressione if let Some(x) = un_valore perché se il valore nella variabile un_valore è None invece che Some, il pattern Some(x) non farà match.
I parametri delle funzioni, le dichiarazioni let e i cicli for possono accettare solo pattern irrefutabili, perché il programma non può fare nulla di significativo quando i valori non fanno match. Le espressioni if let e while let accettano pattern refutabili e irrefutabili, ma il compilatore avverte contro i pattern irrefutabili perché per definizione sono destinati a gestire un possibile fallimento: la funzionalità di una condizionale è nella sua capacità di comportarsi in modo diverso a seconda del successo o del fallimento.
In generale, non dovresti preoccuparti della distinzione tra pattern refutabili e irrefutabili; tuttavia, devi essere familiare con il concetto di refutabilità in modo da poter rispondere quando lo vedi in un messaggio di errore. In quei casi, dovrai cambiare il pattern o il costrutto che stai usando con il pattern, a seconda del comportamento previsto del codice.
Diamo un’occhiata a un esempio di cosa succede quando proviamo a usare un pattern refutabile dove Rust richiede un pattern irrefutabile e viceversa. Il Listato 18-8 mostra una dichiarazione let, ma per il pattern abbiamo specificato Some(x), un pattern refutabile. Come ci si potrebbe aspettare, questo codice non verrà compilato.
rust
[Questo codice non compila!]
let Some(x) = some_option_value;
Listato 18-8: Tentativo di utilizzare un pattern refutabile con let
Se some_option_value fosse un valore None, non farebbe match con il pattern Some(x), il che significa che il pattern è refutabile. Tuttavia, la dichiarazione let può accettare solo un pattern irrefutabile perché non c’è nulla di valido che il codice possa fare con un valore None. Al momento della compilazione, Rust si lamenterà del fatto che abbiamo cercato di utilizzare un pattern refutabile dove è richiesto un pattern irrefutabile:
vbnet
$ 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
--> /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:518:1
|
= note:
/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/option.rs:522:5: not covered
= 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!() };
| ++++++++++ ++++++++++++++++++++++
help: alternatively, you might want to use let else to handle the variant that isn't matched
|
3 | let Some(x) = some_option_value else { todo!() };
| ++++++++++++++++
Per ulteriori informazioni su questo errore, prova `rustc --explain E0005`.
error: could not compile `patterns` due to previous error
Poiché non abbiamo coperto (e non potevamo coprire!) ogni valore valido con il pattern Some(x), Rust produce giustamente un errore del compilatore.
Se abbiamo un pattern refutabile dove è necessario un pattern irrefutabile, possiamo risolvere il problema cambiando il codice che usa il pattern: invece di usare let, possiamo usare if let. Quindi, se il pattern non fa match, il codice salterà semplicemente il codice tra parentesi graffe, dando un modo per continuare in modo valido. Il Listato 18-9 mostra come correggere il codice nel Listato 18-8.
rust
if let Some(x) = some_option_value {
println!("{}", x);
}
Listato 18-9: Utilizzo di if let e un blocco con pattern refutabili invece di let
Abbiamo dato al codice un’alternativa! Questo codice è perfettamente valido, anche se significa che non possiamo usare un pattern irrefutabile senza ricevere un errore. Se diamo a if let un pattern che farà sempre match, come x, come mostrato nel Listato 18-10, il compilatore darà un avviso.
rust
if let x = 5 {
println!("{}", x);
};
Listato 18-10: Tentativo di utilizzare un pattern irrefutabile con if let
Rust si lamenta che non ha senso usare if let con un pattern irrefutabile:
javascript
$ 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: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: `patterns` (bin "patterns") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
Per questo motivo, le braccia di match
devono utilizzare pattern refutabili, tranne per l’ultima arm, che dovrebbe fare match con tutti i valori rimanenti con un pattern irrefutabile. Rust ci consente di usare un pattern irrefutabile in un match
con una sola arm, ma questa sintassi non è particolarmente utile e potrebbe essere sostituita con una dichiarazione let
più semplice.
Ora che sai dove utilizzare i pattern e la differenza tra pattern refutabili e irrefutabili, copriamo tutta la sintassi che possiamo usare per creare pattern.