RustのEnum(列挙型)は、C/C++のenumとは根本的に異なります。関連データを格納でき、状態やエラーのモデリングに最適です。そのため、型安全な有限状態機械やさまざまな種類のOption/Resultを構築し、「sum types」パターンを実装します。歴史的に、同様の構造が関数型言語で厳密に異なる選択肢を持つエンティティを説明するために使用されてきました。
問題: 表現力を得ること(すべての状態の選択肢を表現すること)であり、各処理ケースが必須であり、ブランチを誤ってスキップすることができません。プロジェクト全体のエラーをこのような表現力のある構造なしに型付けすることは難しい。
解決策: 連携データを持つenumとpattern matchingは制御を提供します — 各ブランチはコンパイラによってチェックされ、完全性が保証されます。また、ResultやOptionには既に多くの補助メソッドが実装されています。
コード例:
enum NetworkState { Disconnected, Connecting(u32), // 試行回数 Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Net: disconnected"), NetworkState::Connecting(count) => println!("Net: connecting (attempt {})", count), NetworkState::Connected(addr) => println!("Net: connected to {}", addr), NetworkState::Error(msg) => println!("Net error: {}", msg), } }
主な特徴:
_なしでenumのブランチを部分的に処理できますか?
コンパイラは非完全enumに対して未処理のケースを禁止しますが、_を使用すると未処理のブランチが「吸収」されます。臨床的に重要なブランチでは_を避けるべきであり、将来的な変更が見逃されることがないようにしてください。
pattern matchingで関連値はいつ参照され、いつコピーされますか?
pattern matchingでは、関連データはデフォルトで移動します(move)。参照のみが必要な場合は、参照を使用します:
match &state { NetworkState::Connected(addr) => println!("by ref: {}", addr), _ => {} }
同じ構造体で名前の重複する2つのenumを使用できますか?
できますが、バリエーション名にはenumのプレフィックスが付けられます。これにより衝突が排除され、コードが自己文書化されます(例: Status::Ok vs NetworkState::Ok)。
コードでResult<T, E>の処理は常にcatch-allを介して行われ、新しいエラー(enumの拡張時)が見逃されてしまいます — サイレントエラーが発生します。
長所:
短所:
完全性マッチングを使用して、各Enumバリエーションを明示的に処理するか、安定した動作がないブランチでpanicします。
長所:
短所: