Enum のメソッド
CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 15/61。
構造体と同様に、列挙型(enum)もimplブロック内でメソッドを定義できます。これにより、列挙型に直接振る舞いを持たせることができ、統一されたインターフェースを通じて異なるバリアントを簡単に扱うことが可能になります。
前のレッスンの Message 列挙型にメソッドを追加する方法は次のとおりです:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
impl Message {
fn describe(&self) -> String {
match self {
Message::Quit => String::from("Quit signal"),
Message::Move { x, y } => format!("Move to ({}, {})", x, y),
Message::Write(text) => format!("Message: {}", text),
}
}
}
impl Messageブロックは、構造体の場合とまったく同じように機能します。このメソッドは、列挙型のインスタンスにアクセスするために&selfを受け取ります。メソッド内では、matchを使用して、どのバリアントを扱っているかを判断し、適切な結果を返します。
どのバリアントであっても、メソッドの呼び出し方は同じです:
fn main() {
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
println!("{}", m1.describe()); // 終了シグナル
println!("{}", m2.describe()); // (10, 20) へ移動
}
このパターンは、呼び出し側がどのバリアントを持っているかを知る必要がないため、非常に強力です。単にメソッドを呼び出すだけで、enumが内部で残りの処理を処理します。バリアントに基づいて振る舞いは変わりますが、インターフェースは一貫性を保ちます。
チャレンジ
簡単現在の信号の状態を説明するメソッドを持つenumを使用した、信号機システムを構築しましょう。各信号の色には持続時間に関する情報が含まれ、メソッドが人間が読める形式の説明を提供します。
コードを整理するために、2つのファイルを作成します:
traffic.rs: 以下の3つのバリアントを持つパブリックなTrafficLightenumを定義します:Red— 待機する秒数を表すu32を保持しますYellow— 待機する秒数を表すu32を保持しますGreen— 進む秒数を表すu32を保持します
Stringを返すstatusメソッドを実装します。メソッド内でmatchを使用して各バリアントを処理し、メッセージに持続時間を含めます。main.rs: 作成したtrafficモジュールを取り込み、各信号バリアントのインスタンスを作成し、それぞれのstatusメソッドを呼び出して説明を出力します。
status メソッドは、以下の正確な形式でメッセージを返す必要があります:
Redの場合:Stop for {seconds} secondsYellowの場合:Caution for {seconds} secondsGreenの場合:Go for {seconds} seconds
出力には、各信号の状態をそれぞれの行に表示する必要があります:
Stop for {seconds} seconds
Caution for {seconds} seconds
Go for {seconds} seconds例えば、赤信号が 30 秒、黄信号が 5 秒、青信号が 25 秒の場合、出力は以下のようになります:
Stop for 30 seconds
Caution for 5 seconds
Go for 25 seconds赤信号の持続時間、黄信号の持続時間、青信号の持続時間の3つの入力を受け取ります。
チートシート
列挙型(Enum)は、構造体と同じように、impl ブロック内でメソッドを定義できます。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
impl Message {
fn describe(&self) -> String {
match self {
Message::Quit => String::from("Quit signal"),
Message::Move { x, y } => format!("Move to ({}, {})", x, y),
Message::Write(text) => format!("Message: {}", text),
}
}
}
メソッドは、&self を受け取って列挙型のインスタンスにアクセスし、match を使用して異なるバリアントを処理します:
let m1 = Message::Quit;
let m2 = Message::Move { x: 10, y: 20 };
println!("{}", m1.describe()); // Quit signal
println!("{}", m2.describe()); // Move to (10, 20)
呼び出し側は、どのバリアントであるかに関係なく同じインターフェースを使用します。列挙型は、自身がどのバリアントであるかに基づいて、内部的に振る舞いを処理します。
自分で試してみよう
mod traffic;
use traffic::TrafficLight;
fn main() {
// 各色の継続時間の入力を読み取る
let mut red_input = String::new();
std::io::stdin().read_line(&mut red_input).expect("Failed to read line");
let red_duration: u32 = red_input.trim().parse().expect("Invalid number");
let mut yellow_input = String::new();
std::io::stdin().read_line(&mut yellow_input).expect("Failed to read line");
let yellow_duration: u32 = yellow_input.trim().parse().expect("Invalid number");
let mut green_input = String::new();
std::io::stdin().read_line(&mut green_input).expect("Failed to read line");
let green_duration: u32 = green_input.trim().parse().expect("Invalid number");
// TODO: 入力された継続時間を使用して、各TrafficLightバリアントのインスタンスを作成する
// TODO: 各信号機に対してstatusメソッドを呼び出し、結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。