注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
Rustの enum
は、いくつかのヴァリアントのうちからどれか一つをとるデータを表す型です。
enum Message { Quit, ChangeColor(i32, i32, i32), Move { x: i32, y: i32 }, Write(String), }
各ヴァリアントは、自身に関連するデータを持つこともできます。
ヴァリアントの定義のための構文は、構造体を定義するのに使われる構文と似ており、
(unit-like構造体のような)データを持たないヴァリアント、名前付きデータを持つヴァリアント、(タプル構造体のような)名前なしデータを持つヴァリアントがありえます。
しかし、別々に構造体を定義する場合とは異なり、 enum
は一つの型です。
列挙型の値はどのヴァリアントにもマッチしうるのです。
このことから、列挙型は「直和型」(sum type) と呼ばれることもあります。
列挙型としてとりうる値の集合は、各ヴァリアントとしてとりうる値の集合の和であるためです。
各ヴァリアントの名前を使うためには、 ::
構文を使います。
すなわち、ヴァリアントの名前は enum
自体の名前によってスコープ化されています。
これにより、以下のどちらもうまく動きます。
let x: Message = Message::Move { x: 3, y: 4 }; enum BoardGameTurn { Move { squares: i32 }, Pass, } let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };
どちらのヴァリアントも Move
という名前ですが、列挙型の名前でスコープ化されているため、衝突することなく使うことができます。
列挙型の値は、ヴァリアントに関連するデータに加え、その値自身がどのヴァリアントであるかという情報を持っています。 これを「タグ付き共用体」(tagged union) ということもあります。 データが、それ自身がどの型なのかを示す「タグ」をもっているためです。 コンパイラはこの情報を用いて、列挙型内のデータへ安全にアクセスすることを強制します。 例えば、値をどれか一つのヴァリアントであるかのようにみなして、その中身を取り出すということはできません。
fn main() { fn process_color_change(msg: Message) { // let Message::ChangeColor(r, g, b) = msg; // compile-time error let Message::ChangeColor(r, g, b) = msg; // コンパイル時エラー } }fn process_color_change(msg: Message) { let Message::ChangeColor(r, g, b) = msg; // コンパイル時エラー }
こういった操作が許されないことで制限されているように感じられるかもしれませんが、この制限は克服できます。
それには二つの方法があります。
一つは等値性を自分で実装する方法、もう一つは次のセクションで学ぶ match
式でヴァリアントのパターンマッチを行う方法です。
等値性を実装する方法についてはまだ説明していませんが、 トレイト
のセクションに書いてあります。
列挙型のコンストラクタも、関数のように使うことができます。 例えばこうです。
fn main() { enum Message { Write(String), } let m = Message::Write("Hello, world".to_string()); }let m = Message::Write("Hello, world".to_string());
これは、以下と同じです。
fn main() { enum Message { Write(String), } fn foo(x: String) -> Message { Message::Write(x) } let x = foo("Hello, world".to_string()); }fn foo(x: String) -> Message { Message::Write(x) } let x = foo("Hello, world".to_string());
このことは今すぐ役立つことではないのですが、クロージャ
のセクションでは関数を他の関数へ引数として渡す話をします。
例えば、これを イテレータ
とあわせることで、 String
のベクタから Message::Write
のベクタへ変換することができます。
let v = vec!["Hello".to_string(), "World".to_string()]; let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();