Menu
Coddy logo textTech

データバリアントのマッチング

CoddyのRustジャーニー「Object Oriented Programming」セクションの一部 — レッスン 16/61。

前のレッスンでは、異なるバリアントを処理するためにenumのメソッド内でmatchを使用しました。次は、matchが実際にそれらのバリアント内に格納されたデータをどのように抽出するのか、構造分解(destructuring)と呼ばれる手法に焦点を当てましょう。

データを保持するenumのバリアントに対してマッチングを行うとき、そのデータをパターン内で直接変数にバインドすることができます:

enum Event {
    Click { x: i32, y: i32 },
    KeyPress(char),
    Resize(u32, u32),
}

fn handle_event(event: Event) {
    match event {
        Event::Click { x, y } => {
            println!("Clicked at ({}, {})", x, y);
        }
        Event::KeyPress(key) => {
            println!("Key pressed: {}", key);
        }
        Event::Resize(width, height) => {
            println!("Resized to {}x{}", width, height);
        }
    }
}

各アームは、バリアントの構造に基づいて異なる方法でデータを抽出します。構造体形式のバリアントの場合、{ field_name } を使用して名前付きフィールドを取り出します。タプル形式のバリアントの場合、(width, height) のような位置変数を使用します。

選択した変数名は、そのmatchアームの中で利用可能になります。どのバリアントであるかを確認すること、そのペイロードにアクセスすることを同時に行えるため、これは強力です。

コンパイラがすべてのバリアントを確実に処理するようにし、抽出された値はすぐに使用できます。追加の手順は必要ありません。

challenge icon

チャレンジ

簡単

さまざまな種類のユーザーコマンドを処理するコマンドプロセッサを構築しましょう。各コマンドは異なるデータを保持しており、プロセッサはパターンマッチングを使用してそのデータを抽出し、適切なレスポンスを生成します。

コードを整理するために、2つのファイルを作成します:

  • command.rs: 3つのバリアントを持つパブリックな Command 列挙型を定義します:
    • Say — 1つの String メッセージを保持します(タプル構文)
    • Move — 名前付きフィールド direction (String) と steps (u32) を保持します
    • Calculate — 加算する2つの i32 値を保持します(タプル構文)
    次に、match を使用して各バリアントをデストラクト(構造分解)し、実行されたアクションを説明する String を返す execute メソッドを実装します。
  • main.rs: 作成したコマンドモジュールを取り込み、提供された入力を使用して各コマンドバリアントのインスタンスを1つずつ作成し、それぞれに対して execute メソッドを呼び出して結果を出力します。

execute メソッドは、各バリアントをデストラクトしてそのデータにアクセスし、以下の正確な形式でメッセージを返す必要があります:

  • Say の場合: Saying: {message}
  • Move の場合: Moving {steps} steps {direction}
  • Calculate の場合: {a} + {b} = {sum}

出力は、各コマンドの結果をそれぞれの行に表示する必要があります:

Saying: {message}
Moving {steps} steps {direction}
{a} + {b} = {sum}

例えば、sayメッセージが "Hello world"、移動が 5 ステップで "north"、計算が 1025 の場合、出力は以下のようになります:

Saying: Hello world
Moving 5 steps north
10 + 25 = 35

入力として、sayメッセージ、方向、ステップ数、および計算する2つの数値の計5つを受け取ります。

チートシート

match によるパターンマッチングを使用すると、enumのバリアントをデストラクト(構造分解)し、そのデータをパターン内で直接抽出できます。

タプルバリアントの場合は、位置に基づいた変数を使用します:

enum Event {
    KeyPress(char),
    Resize(u32, u32),
}

match event {
    Event::KeyPress(key) => {
        println!("Key: {}", key);
    }
    Event::Resize(width, height) => {
        println!("{}x{}", width, height);
    }
}

構造体形式のバリアントの場合は、名前付きフィールドの構文を使用します:

enum Event {
    Click { x: i32, y: i32 },
}

match event {
    Event::Click { x, y } => {
        println!("Clicked at ({}, {})", x, y);
    }
}

抽出された変数は、それぞれのマッチアーム内で利用可能になり、バリアントの型のチェックとデータへのアクセスを同時に行うことができます。

自分で試してみよう

mod command;

use command::Command;

fn main() {
    // 入力を読み込む
    let mut say_message = String::new();
    std::io::stdin().read_line(&mut say_message).expect("Failed to read line");
    let say_message = say_message.trim().to_string();

    let mut direction = String::new();
    std::io::stdin().read_line(&mut direction).expect("Failed to read line");
    let direction = direction.trim().to_string();

    let mut steps_input = String::new();
    std::io::stdin().read_line(&mut steps_input).expect("Failed to read line");
    let steps: u32 = steps_input.trim().parse().expect("Failed to parse steps");

    let mut num1_input = String::new();
    std::io::stdin().read_line(&mut num1_input).expect("Failed to read line");
    let num1: i32 = num1_input.trim().parse().expect("Failed to parse num1");

    let mut num2_input = String::new();
    std::io::stdin().read_line(&mut num2_input).expect("Failed to read line");
    let num2: i32 = num2_input.trim().parse().expect("Failed to parse num2");

    // TODO: say_message を使用して Say コマンドを作成し、実行する
    
    // TODO: direction と steps を使用して Move コマンドを作成し、実行する
    
    // TODO: num1 と num2 を使用して Calculate コマンドを作成し、実行する
    
    // TODO: 各コマンドの実行結果を出力する
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン