【Rust入門】列挙型の基本を分かりやすく解説

Rust の列挙型(enum
)の基本を初心者にも分かりやすく解説します。
Rust の列挙体(enum
)
列挙型(enum
)とは
Rust の列挙型(enum
)とは、「複数の選択肢のうち、どれか1つを表現したい」ときに使える型です。Rust には構造体という型を定義する仕組みがありますが、構造体はデータがどういった構成をしているかを表すのに対して、列挙型は選択肢のいずれか1つの値を表現するという点で違いがあります。例えば「日曜~土曜のいずれか」のような選択肢は代表的な例でしょう。これらの列挙された値を「バリアント(列挙子)」と呼びます。
enum
は、C 言語や Java など多くのプログラミング言語にも存在していますが、Rust の列挙型が特に強力なのは、各バリアントごとにデータを持たせることができるという点です。つまり、単に名前がついた定数の集まりではなく、それぞれのバリアントが異なるデータ構造を持つことができる柔軟な型として機能します。これらは、Rust のパターンマッチングの機能と連携することで非常に強力な表現力と安全なコーディングを提供します。
この記事では、列挙型の基本的な定義の方法や代表的な列挙型の例である Option<T>
型と Result<T, E>
型について説明します。パターンマッチングと連携した活用方法については別記事でまたまとめてご紹介します。
基本的な列挙型
まずは、基本的な列挙型の定義方法を見ていきましょう。列挙型は、enum
キーワードを使って定義し、ブロック内でバリアントを列挙します。列挙型名やバリアント名は、CamelCase
(単語の先頭が大文字)で定義するのが慣習です。
// 曜日を表す列挙体 enum WeekDay { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, } fn main() { // 変数に束縛 let today = WeekDay::Sunday; }
列挙型のバリアントを使用する際には「列挙型名::バリアント名
」という形で使用します。上記では、today 変数に WeekDay::Sunday ということで日曜日を表すバリアントを束縛しています。
値を持つ列挙型
上記で使用したような使い方は、他のプログラミングでも一般的に使用できる方法です。Rust の enum
では、異なる型や構造のデータを持つことができます。
例えば、あるシステムで扱うメッセージを表す Message
型を以下のように定義できます。
// メッセージを表す列挙型 enum Message { Quit, Echo(String), Move(i32, i32), ChangeColor{r: u8, g: u8, b: u8, a: f32}, } fn main() { // 終了を伝えるメッセージ let quit = Message::Quit; // 文字列を表示するメッセージ let echo = Message::Echo(String::from("メッセージ")); // (x, y) = (5, 10) への移動を表すメッセージ let move_position = Message::Move(5, 10); // 色を変更する let change_color = Message::ChangeColor { r: 255, g: 255, b: 0, a: 0.5 }; }
上記例では、以下のような種類のバリアントを作成しています。
Quit
:通常のバリアントEcho
:タプル形式で単一の String 型を持つバリアントMove
:タプル形式で複数の型を持つバリアントChangeColor
:名前付きフィールド(構造体形式)で各型を持つバリアント
このように値付きでバリアントを持たせると、状態毎により異なるメッセージを統一的に Message
型で扱うことができ、設計や保守性が向上します。
重要な列挙型(Option<T>
型と Result<T, E>
型)
Rust の列挙体で標準で用意されているものにおいてとても重要な列挙型として、Option<T>
型と Result<T, E>
型があります。これらの使い方は別途まとめてみたいと思いますが、ここでは概要を紹介します。(※ T
や E
は「ジェネリクス」と呼ばれ、後から使う側が具体的な型を指定できる仕組みです。)
値の有無を表現する Option<T>
型
Rust では、他のプログラミングにおける null
のようなものはありません。Rust では、ある値が「あるかもしれないし、ないかもしれない」という状況を Option<T>
型 で表現できます。
enum Option<T> { Some(T), // 値が存在する (T は任意の型) None, // 値が存在しない }
fn main() { let name: Option<String> = Some(String::from("山田太郎")); let empty: Option<String> = None; }
Option<T>
型は、値がある場合は「Some(T)
」、値がない場合は「None
」として型が定義されています。上記使用例では、T
の部分を String
であった場合です。
C などの他の言語では、null
を一旦代入しておくということが多いですが、nullのままであることを忘れて後続の処理を書いてしまうと予期せぬバグにつながります。
Rust では、Option
型で存在しないかもしれないことを明示的に表現することで、プログラマは None
である場合について向き合い処理を記載しない限りコンパイルを通すことができなくなります。
成功と失敗を表現する Result<T, E>
型
Option<T>
型と同様に非常に重要な型として、Result<T, E>
型があります。Rust では、他のプログラミング言語である例外という考え方がありません。その代わりに、この Result<T, E>
型のような型を使用します。この型は、関数等の戻り値として非常によく利用され「処理が成功したか、失敗したか」を表現します。
enum Result<T, E> { Ok(T), // 処理が成功 T は成功時の値の型 Err(E), // 処理が失敗 E はエラー情報を含む型 }
fn main() { let result: Result<i32, &str> = Ok(42); let error: Result<i32, &str> = Err("処理に失敗しました"); }
Result<T, E>
型は、成功した場合は「Ok(T)
」、処理が失敗した場合は「Err(E)
」として型が定義されています。上記使用例では、T
が i32
なので成功したら i32
型の値が返却され、E
は &str
なので処理が失敗した場合には失敗メッセージが返ってくるような場合です。
こちらも Option<T>
型の場合と同じで、
という1つの型で成功と失敗を明示的に表現しています。そのため、この型の返却値を受け取った処理では、成功と失敗の例に向き合わなければいけないわけです。Result<T, E>
型
上記の Option<T>
型や
は、明示的に型で扱うことでコンパイラでもバグを発見しやすくなり、コードの安全性を格段に向上させます。これらの型をより活用するためには、Rust の「パターンマッチング」機能と組み合わせるのが非常に強力です。別途、パターンマッチの書き方や、Result<T, E>
型Option<T>
や
をより便利に扱う方法は紹介していきます。Result<T, E>
まとめ
この記事では、Rust における列挙型(enum
)の基本的な使い方や、値を持つバリアントの表現方法、そして特によく使われる Option<T>
型と Result<T, E>
型について紹介しました。
列挙型は「複数の状態のうちの1つ」を安全かつ柔軟に表現できる強力な仕組みです。バリアントごとに異なるデータを持たせることができる点が、Rust の列挙型の大きな特徴です。
Option<T>
や Result<T, E>
は、値の有無や成功・失敗といった状態を明示的に型で表現でき、バグの発見をコンパイラが手助けしてくれるという大きなメリットがあります。
これらの列挙型をより活用するためのパターンマッチングや便利なメソッドの使い方についても紹介していく予定です。