RustのEnumとは?
RustのEnum(列挙型)は、複数の種類の値をひとつの型として表現できる強力な機能です。
他の言語のEnumと異なり、Rustのenumは各バリアント(種類)にデータを持たせることができます。これにより、OptionやResultといった標準ライブラリの中核を担う型も、Enumとして実装されています。
まずシンプルな例から見てみましょう。
enum Direction {
North,
South,
East,
West,
}
fn main() {
let dir = Direction::North;
}
これだけなら他言語と変わりませんが、Rustではバリアントにデータを付加できます。
enum Message {
Quit, // データなし
Move { x: i32, y: i32 }, // 名前付きフィールド
Write(String), // タプル構造体風
ChangeColor(u8, u8, u8), // 複数の値
}
Message::Moveは座標を持ち、Message::Writeは文字列を持ちます。一つの型でこれだけ多様な表現が可能なのがRustのEnumの強みです。
match式でパターンマッチング
Enumを活用するにはmatch式が欠かせません。matchはすべてのバリアントを網羅的に処理することを強制するため、処理漏れをコンパイル時に防げます。
fn handle_message(msg: Message) {
match msg {
Message::Quit => {
println!("終了します");
}
Message::Move { x, y } => {
println!("({}, {}) に移動します", x, y);
}
Message::Write(text) => {
println!("メッセージ: {}", text);
}
Message::ChangeColor(r, g, b) => {
println!("色を ({}, {}, {}) に変更します", r, g, b);
}
}
}
バリアントに応じてデータを取り出しながら処理を分岐できます。もし一つでも処理を書き忘れると、コンパイルエラーになるので安全です。
_(ワイルドカード)でその他をまとめる
すべてのケースを個別に書かずに、残りをまとめて処理したい場合は_を使います。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value(coin: Coin) -> u32 {
match coin {
Coin::Quarter => 25,
_ => 1, // Quarter以外はすべて1
}
}
Option型を理解する
Rustにはnullがありません。代わりに値があるかないかを表現するためにOption<T>というEnumが用意されています。
enum Option<T> {
Some(T), // 値がある
None, // 値がない
}
たとえばリストの先頭要素を取り出す関数は、要素がない場合も考慮してこう書けます。
fn first_element(list: &[i32]) -> Option<i32> {
if list.is_empty() {
None
} else {
Some(list[0])
}
}
fn main() {
let numbers = vec![10, 20, 30];
match first_element(&numbers) {
Some(n) => println!("先頭の値: {}", n),
None => println!("リストは空です"),
}
let empty: Vec<i32> = vec![];
match first_element(&empty) {
Some(n) => println!("先頭の値: {}", n),
None => println!("リストは空です"),
}
}
出力:
先頭の値: 10
リストは空です
if letで簡潔に書く
matchを使わず、特定のバリアントだけ処理したい場合はif letが便利です。
let some_value: Option<i32> = Some(42);
if let Some(v) = some_value {
println!("値は {}", v);
}
Noneの場合は何もしない、という意図が明確に伝わります。
Enumにメソッドを実装する
Enumにもimplブロックを使ってメソッドを追加できます。
enum TrafficLight {
Red,
Yellow,
Green,
}
impl TrafficLight {
fn duration_secs(&self) -> u32 {
match self {
TrafficLight::Red => 60,
TrafficLight::Yellow => 5,
TrafficLight::Green => 45,
}
}
fn is_stop(&self) -> bool {
matches!(self, TrafficLight::Red)
}
}
fn main() {
let light = TrafficLight::Red;
println!("待ち時間: {} 秒", light.duration_secs()); // 60 秒
println!("停止が必要: {}", light.is_stop()); // true
}
matches!マクロは、特定のパターンかどうかをboolで返す便利なマクロです。
まとめ
| 機能 | 用途 |
|---|---|
enum | 複数種の値を一つの型で表現 |
match | 網羅的なパターンマッチング |
Option<T> | null安全な「値あり/なし」の表現 |
if let | 特定バリアントだけを簡潔に処理 |
impl on enum | Enumへのメソッド追加 |
RustのEnumとパターンマッチングは、型安全性を保ちながら複雑なロジックを整理するための核心的な機能です。OptionやResult(エラーハンドリング)もすべてEnumで構成されており、これをマスターするとRustコード全体の読み書きが格段にスムーズになります。ぜひ積極的に活用してみてください!