このページの目次
enumとパターンマッチング
Rustのenumはタグ付き共用体(tagged union)であり、各variantは独立したデータを持ち、コンパイラがmatchで全てのvariantを網羅していることを保証します(網羅性チェック)。OptionとResultは言語組み込みではなく、標準ライブラリがenumを用いて定義しています——これは、enumがRustで最も表現力豊かな型構築子であることを証明しています。
enumはtagged unionであり、単なる「列挙」ではない
Cのenumは単なる名前付き整数定数の集合に過ぎません。Rustのenumの各variantは異なるデータを持てる——それはタグ付き共用体です:
コンパイラは Message に対して、最大のvariant(Move = i32が2つ = 8バイト)を格納するのに十分なサイズを割り当て、さらにdiscriminant(タグ)フィールドを追加します。Option<&T> のようなnicheが存在するケースでは、タグの最適化が行われます。
Option: Rustにnullはない
Tony Hoareはnullを「10億ドルのミス」と呼んでいます。Rustにはnullがありません——存在しないかもしれない値は Option<T> で表されます:
// 呼び出し側はNoneの場合を処理しなければならない:
match safe_div
コンパイラは None の場合の処理を強制します——チェックを忘れることは不可能です。Option<T> は特殊なnullマーカではなく通常の型なので、Option に対してmap、and_thenなどのcombinatorを使用できます。
Result: エラーも値である
例外の問題は、制御フローを壊してしまうことです——関数のシグネチャからは、どのような例外を投げる可能性があるか分かりません。Result<T, E> はエラーを戻り値の一部にします:
呼び出し側は Result<i32, ParseIntError> を見ることで——この関数は成功時に i32 を返し、失敗時に解析エラーを返す可能性がある——ことが分かります。隠れた制御フローはありません。
Match: コンパイル時の網羅性チェック
match number
コンパイラは、matchが全ての可能なvariantを網羅しているかどうかをチェックします。型に新しいvariantが追加された場合(例えばenumに新しいメンバーが追加された場合)、その型に対するmatchが書かれている箇所はすべてコンパイルに失敗します——新しいケースを明示的に処理することを強制します。
パターンの様々な形式
// デストラクチャリング
let = ;
let Point = point;
// refパターン: moveせず、借用する
let ref r = value; // r: &T, valueはmoveされない
// match guard: 追加の条件
match num
// @バインディング: パターンと変数バインディングを同時に実行
match opt
// if let / while let: 1つのvariantのみに関心がある場合のシンタックスシュガー
if let Some = opt
while let Some = iter.next
参考
- Rust Book: 第6章 (Enums)、第18章 (Patterns)
- Rust Reference: Match expressions, Patterns
Keywords: enum, Option, Result, match, pattern, if let, while let, exhaustiveness, tagged union