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

反駁可能性: パターンがマッチに失敗する可能性があるかどうか

パターンには、反駁可能なものと反駁不可能なものの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_valueNone 値であれば、パターン 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 文で置き換えられます。

これで、パターンをどこで使うのか、そして反駁可能なパターンと反駁不可能なパターンの違いがわかったので、パターンを作るために使えるすべての構文を見ていきましょう。