反駁可能性: パターンがマッチに失敗する可能性があるかどうか
パターンには、反駁可能なものと反駁不可能なものの2種類があります。渡される可能性のあるどんな値にもマッチするパターンは、反駁不可能 です。例としては、文 let x = 5; における x が挙げられます。x はどんなものにもマッチするため、マッチに失敗することがありません。ある取りうる値に対してはマッチに失敗しうるパターンは、反駁可能 です。例としては、式 if let Some(x) = a_value における Some(x) が挙げられます。というのも、変数 a_value の値が Some ではなく None であれば、Some(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;
}
もし 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 を使う代わりに、let...else を使えるのです。そうすれば、パターンがマッチしなかったときに、中括弧内のコードがその値を処理します。リスト19-9は、リスト19-8のコードを修正する方法を示しています。
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value else {
return;
};
}
これで、このコードには逃げ道が与えられました! このコードは完全に有効です。ただし、その代わり、警告を受けずに反駁不可能なパターンを使うことはできません。リスト19-10に示すように、常にマッチする x のようなパターンを let...else に与えると、コンパイラは警告を出します。
fn main() {
let x = 5 else {
return;
};
}
Rust は、反駁不可能なパターンと一緒に let...else を使うことには意味がないと指摘します。
$ 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 では、アームが1つしかない match で反駁不可能なパターンを使うこともできますが、この構文は特に有用ではなく、より単純な let 文で置き換えられます。
これで、パターンをどこで使うのか、そして反駁可能なパターンと反駁不可能なパターンの違いがわかったので、パターンを作るために使えるすべての構文を見ていきましょう。