Rustのmatch文で式を評価し値を計算する方法を解説

Rustのmatch文は、条件分岐や値の分類に加え、式を評価して結果を計算する機能を備えています。これは、柔軟で読みやすいコードを書く上で非常に便利です。特に、複雑な条件分岐を効率的に処理したい場合に、match文を活用することで、コードの簡潔性と可読性を両立できます。

本記事では、Rustのmatch文を使った式評価の方法を基礎から応用まで詳しく解説します。match文の基本構文から、具体的な計算例やパターンマッチングを活用した実践的な手法、さらにはネストや複雑な条件への対応方法についても触れます。これを読めば、Rustプログラムでのmatch文の活用法が一段と深まるでしょう。

目次

Rustの`match`文の基本


Rustのmatch文は、特定の値を条件に応じて分類し、適切な処理を行うための強力な制御構造です。Rustでは、この文を使用することで、簡潔かつ安全に条件分岐を実装できます。

`match`文の構文


match文の基本構文は以下のようになります。

match 値 {
    パターン1 => 処理1,
    パターン2 => 処理2,
    _ => デフォルト処理,
}
  • : 条件を判定する対象となる値を指定します。
  • パターン: 値を分類する条件を記述します。
  • 処理: 条件に一致した場合に実行される式または文です。
  • デフォルト処理: すべてのパターンに一致しない場合に実行されます(省略可能ではありませんが、!型の値では不要です)。

基本的な使用例


以下の例は、数値に基づいて異なる処理を行うmatch文の基本的な例です。

fn main() {
    let number = 3;

    match number {
        1 => println!("One!"),
        2 => println!("Two!"),
        3 => println!("Three!"),
        _ => println!("Something else!"),
    }
}

このコードでは、変数numberの値が1から3に応じて異なるメッセージを表示し、それ以外の場合はデフォルトメッセージを表示します。

パターンのマッチング


match文では以下のような多様なパターンをサポートしています。

  • 単一の値: 1, "text"などの具体的な値。
  • 範囲: 1..=5(1から5を含む)。
  • 複数パターン: 1 | 2 | 3(1、2、または3の場合)。
  • 構造体やタプル: 複雑なデータ構造を分解してマッチング可能。

例: 範囲を使ったパターンマッチング

fn main() {
    let number = 7;

    match number {
        1..=5 => println!("Between 1 and 5"),
        6..=10 => println!("Between 6 and 10"),
        _ => println!("Out of range"),
    }
}

このように、match文を活用することで、条件分岐を簡潔に表現することができます。

`match`文内で式を評価する仕組み

Rustのmatch文では、条件に一致するパターンに応じて式を評価し、その結果を利用することが可能です。これは、条件分岐だけでなく、計算やデータ処理を効率化するために非常に有用です。

式の評価とは


match文の各分岐では、値ではなく式を指定することができます。Rustでは式が値を生成するため、match文を使うと条件に応じた計算や処理結果を直接返すことができます。

fn main() {
    let number = 5;

    let result = match number {
        1 => "One".to_string(),
        2 | 3 => format!("Two or Three: {}", number),
        _ => (number * 2).to_string(),
    };

    println!("{}", result);
}

この例では、numberの値によって異なる式が評価され、その結果が変数resultに格納されます。

式評価の流れ

  1. match文が評価される値を取得: 条件となる値(上記例ではnumber)。
  2. パターンのマッチング: 各分岐のパターンが左から順に評価される。最初に一致したパターンが選ばれる。
  3. 対応する式の評価: 一致した分岐内の式が評価され、その結果が返される。

複雑な式の利用


以下は、計算結果を返す式を含むmatch文の例です。

fn main() {
    let score = 85;

    let grade = match score {
        90..=100 => "A",
        80..=89 => "B",
        70..=79 => "C",
        60..=69 => "D",
        _ => "F",
    };

    println!("Your grade is: {}", grade);
}

この例では、スコアscoreに基づいて評価が行われ、対応する文字列がgradeに格納されます。

式評価のポイント

  1. すべての分岐が同じ型を返す必要がある
    Rustでは型安全性を確保するため、match文のすべての分岐で返す値の型を一致させる必要があります。
let value = match 10 {
    1 => "one",
    _ => "unknown",
};
  1. 処理がpanic!を含む場合の注意
    条件によって処理を中断する際にも、式評価のルールが適用されます。
let value = match some_condition {
    true => 42,
    false => panic!("Invalid state"),
};

実践例: 式評価と値の返却


match文を活用して、特定の条件に基づく計算を実装する例を見てみましょう。

fn calculate_discount(price: u32) -> u32 {
    match price {
        0..=100 => 5, // 5%の割引
        101..=500 => 10, // 10%の割引
        _ => 15, // 15%の割引
    }
}
fn main() {
    let price = 150;
    let discount = calculate_discount(price);

    println!("Discount: {}%", discount);
}

このように、match文内で式を評価することで、複雑な条件分岐を簡潔に書けるのが特徴です。

条件に応じた値の計算例

Rustのmatch文は、条件に基づいて値を計算し、処理の効率を高めることができます。以下では、具体的なコード例を用いて、match文で条件に応じた値を計算する方法を解説します。

数値に基づく計算


以下は、数値に応じて異なる計算を行う例です。

fn main() {
    let number = 8;

    let result = match number {
        1..=5 => number * 10,
        6..=10 => number + 100,
        _ => number - 20,
    };

    println!("The result is: {}", result);
}

この例では、numberの値に応じて以下のような計算が行われます:

  • 1から5の場合:number * 10
  • 6から10の場合:number + 100
  • それ以外の場合:number - 20

文字列の生成


条件に基づいて異なる文字列を生成する場合も、match文を活用できます。

fn main() {
    let day = "Monday";

    let activity = match day {
        "Monday" => "Go to the gym",
        "Friday" => "Watch a movie",
        "Sunday" => "Relax at home",
        _ => "Work",
    };

    println!("Today's activity: {}", activity);
}

ここでは、曜日に基づいて異なる活動内容を計算しています。

計算ロジックの抽象化


以下は、商品の価格に基づいて割引額を計算する例です。

fn calculate_discount(price: u32) -> u32 {
    match price {
        0..=100 => price / 10, // 10%割引
        101..=500 => price / 5, // 20%割引
        _ => price / 2, // 50%割引
    }
}

fn main() {
    let price = 250;
    let discount = calculate_discount(price);

    println!("The discount is: {} yen", discount);
}

この例では、商品の価格帯に応じた割引額が計算されます。

複数条件の計算


複数条件を組み合わせて計算する場合も、match文は非常に効果的です。

fn main() {
    let temperature = 30;
    let humidity = 70;

    let weather_description = match (temperature, humidity) {
        (t, h) if t > 30 && h > 80 => "Hot and Humid",
        (t, _) if t > 30 => "Hot",
        (_, h) if h > 80 => "Humid",
        _ => "Pleasant",
    };

    println!("The weather is: {}", weather_description);
}

ここでは、気温と湿度に基づいて異なる天気の説明を生成しています。

まとめ


match文は、値を分類するだけでなく、その分類に基づいて計算を行う柔軟なツールです。数値や文字列、複数条件を使った計算など、様々な場面で利用できます。この特性を活用することで、コードの効率性と可読性を向上させることができます。

式評価と返り値の活用

Rustのmatch文は、式を評価した結果を返り値として活用できる柔軟な制御構造です。これにより、条件分岐に基づいて動的に値を計算し、その結果を関数の戻り値や他の処理に直接利用することが可能です。

関数の戻り値としての活用


以下は、match文で評価した結果を関数の戻り値として利用する例です。

fn categorize_age(age: u8) -> &'static str {
    match age {
        0..=12 => "Child",
        13..=19 => "Teenager",
        20..=64 => "Adult",
        _ => "Senior",
    }
}

fn main() {
    let age = 30;
    let category = categorize_age(age);

    println!("Age category: {}", category);
}

ここでは、年齢を入力として受け取り、そのカテゴリー(子供、ティーンエイジャー、大人、シニア)を返す関数を定義しています。

変数への代入としての活用


match文で計算した結果を変数に代入し、後続の処理で利用することもできます。

fn main() {
    let score = 75;

    let grade = match score {
        90..=100 => "A",
        80..=89 => "B",
        70..=79 => "C",
        60..=69 => "D",
        _ => "F",
    };

    println!("Your grade is: {}", grade);
}

この例では、スコアに基づいて成績を計算し、その結果を変数gradeに代入しています。

ネストした`match`文の返り値活用


複雑な条件分岐を処理する場合、ネストしたmatch文を使用して計算結果を返すこともできます。

fn evaluate(input: i32) -> String {
    match input {
        1..=10 => match input % 2 {
            0 => "Even number".to_string(),
            _ => "Odd number".to_string(),
        },
        _ => "Out of range".to_string(),
    }
}

fn main() {
    let value = 7;
    let result = evaluate(value);

    println!("Result: {}", result);
}

この例では、範囲と偶数/奇数の条件を組み合わせて結果を返しています。

演算結果の返却


計算結果そのものを直接返すことも可能です。

fn calculate_area(shape: &str, size: u32) -> u32 {
    match shape {
        "square" => size * size,
        "circle" => (3.14 * (size as f64).powi(2)) as u32,
        _ => 0,
    }
}

fn main() {
    let shape = "square";
    let size = 5;

    let area = calculate_area(shape, size);

    println!("The area of the {} is: {}", shape, area);
}

この例では、図形の種類と大きさに基づいて面積を計算し、その値を直接返しています。

型の整合性の重要性


match文で返す値は、すべて同じ型である必要があります。Rustの型システムにより、すべての分岐が同じ型を返していない場合にはコンパイルエラーとなります。以下はコンパイルエラーの例です:

// このコードはコンパイルエラーになります
let result = match 10 {
    1 => "string",
    _ => 42, // 型が一致しない
};

このような場合には、すべての分岐が同じ型を返すように修正する必要があります。

まとめ


match文を使って条件に応じた値を計算し、その結果を返り値として利用することで、コードを簡潔で効率的にすることができます。また、関数の戻り値や変数への代入として活用することで、柔軟なロジックを実現できます。Rustの型システムを活かしながら、安全にこの機能を活用しましょう。

ネストした`match`文での計算

Rustのmatch文は、ネストして使用することで複雑な条件分岐にも対応できます。ネストすることで、階層的に条件を処理し、柔軟なロジックを実現することが可能です。ただし、コードが複雑になるため、読みやすさを意識した設計が重要です。

ネストした`match`文の基本構造


以下は、ネストしたmatch文の基本例です。

fn main() {
    let value = 7;

    let result = match value {
        1..=10 => match value % 2 {
            0 => "Even number in range 1-10",
            _ => "Odd number in range 1-10",
        },
        _ => "Out of range",
    };

    println!("{}", result);
}

この例では、まずvalueが1~10の範囲にあるかをチェックし、その後さらに偶数か奇数かを判定しています。

実践例: 入れ子の条件で処理を分ける


以下は、点数に基づいて成績と特別な評価を計算する例です。

fn evaluate_score(score: u32) -> &'static str {
    match score {
        90..=100 => match score {
            95..=100 => "A+",
            _ => "A",
        },
        80..=89 => "B",
        70..=79 => "C",
        60..=69 => "D",
        _ => "F",
    }
}

fn main() {
    let score = 92;
    let grade = evaluate_score(score);

    println!("Your grade is: {}", grade);
}

この例では、スコアが90以上の場合さらに細かく評価を分けています。

ネストの入れ子による階層的な計算


以下は、天候データ(気温と降水量)に基づいて条件を分岐させる例です。

fn describe_weather(temperature: i32, rainfall: u32) -> &'static str {
    match temperature {
        t if t < 0 => "Freezing",
        0..=15 => match rainfall {
            0 => "Cold and dry",
            1..=50 => "Cold and rainy",
            _ => "Cold and stormy",
        },
        _ => match rainfall {
            0 => "Warm and dry",
            1..=50 => "Warm and rainy",
            _ => "Warm and stormy",
        },
    }
}

fn main() {
    let temperature = 10;
    let rainfall = 30;

    let weather = describe_weather(temperature, rainfall);

    println!("Weather description: {}", weather);
}

ここでは、気温と降水量を基にして詳細な条件分岐を実装しています。

ネストの注意点と改善方法

  1. 読みやすさを意識する
    ネストが深くなると、コードが読みにくくなる可能性があります。その場合、条件を関数や変数に分けて処理を簡潔にすることが重要です。
fn is_even(value: i32) -> &'static str {
    match value % 2 {
        0 => "Even",
        _ => "Odd",
    }
}

fn evaluate_value(value: i32) -> &'static str {
    match value {
        1..=10 => is_even(value),
        _ => "Out of range",
    }
}

fn main() {
    let value = 7;
    println!("{}", evaluate_value(value));
}
  1. 関数でロジックを分離
    複雑なネストを避けるために、個別の条件ごとに関数を作成すると、コードが簡潔で再利用可能になります。

まとめ


ネストしたmatch文を使用すると、複雑な条件を階層的に処理でき、柔軟なロジックを実現できます。ただし、過剰にネストを使用するとコードが読みづらくなるため、必要に応じて関数化やリファクタリングを行い、コードの可読性を保つ工夫が重要です。

複雑な条件の評価と計算

Rustのmatch文は、複雑な条件分岐を柔軟に処理できます。条件を整理し、計算結果を生成することで、効率的かつ直感的なコードを実現可能です。以下に、複雑な条件を扱う実践的な手法を示します。

複数条件の組み合わせ


match文では、複数条件を組み合わせて1つのパターンとして扱えます。以下は、条件をifガードで補足した例です。

fn evaluate_temperature(temp: i32, is_sunny: bool) -> &'static str {
    match temp {
        t if t > 30 && is_sunny => "Hot and sunny",
        t if t > 30 => "Hot but cloudy",
        15..=30 if is_sunny => "Warm and sunny",
        15..=30 => "Warm and cloudy",
        _ if is_sunny => "Cold but sunny",
        _ => "Cold and cloudy",
    }
}

fn main() {
    let temp = 25;
    let is_sunny = true;

    let weather = evaluate_temperature(temp, is_sunny);

    println!("The weather is: {}", weather);
}

ここでは、気温と晴れ/曇りの条件を組み合わせ、詳細な天気の説明を生成しています。

タプルを用いた条件の評価


複数の変数をタプルとして扱うことで、直感的に複雑な条件を整理できます。

fn evaluate_score(score: u32, bonus: bool) -> &'static str {
    match (score, bonus) {
        (90..=100, true) => "Excellent with bonus",
        (90..=100, false) => "Excellent",
        (75..=89, true) => "Good with bonus",
        (75..=89, false) => "Good",
        (_, true) => "Improved with bonus",
        _ => "Needs improvement",
    }
}

fn main() {
    let score = 85;
    let bonus = true;

    let evaluation = evaluate_score(score, bonus);

    println!("Evaluation: {}", evaluation);
}

この例では、点数とボーナスの有無を組み合わせた評価を行っています。

列挙型のパターンマッチング


列挙型を使用すると、さらに意味のある複雑な条件を扱うことができます。

enum UserRole {
    Admin,
    Moderator,
    User,
    Guest,
}

fn access_level(role: UserRole, logged_in: bool) -> &'static str {
    match (role, logged_in) {
        (UserRole::Admin, true) => "Full access",
        (UserRole::Moderator, true) => "Moderate access",
        (UserRole::User, true) => "Basic access",
        (_, true) => "Limited access",
        (_, false) => "No access",
    }
}

fn main() {
    let role = UserRole::Moderator;
    let logged_in = true;

    let level = access_level(role, logged_in);

    println!("Access level: {}", level);
}

この例では、ユーザーの役割とログイン状態に応じたアクセス権を計算しています。

計算を伴う条件評価


match文で式を利用し、条件評価と計算を同時に行うことも可能です。

fn calculate_shipping_cost(weight: u32, express: bool) -> u32 {
    match (weight, express) {
        (0..=5, false) => 500,
        (0..=5, true) => 1000,
        (6..=20, false) => 1000,
        (6..=20, true) => 2000,
        (_, false) => 3000,
        (_, true) => 5000,
    }
}

fn main() {
    let weight = 8;
    let express = true;

    let cost = calculate_shipping_cost(weight, express);

    println!("Shipping cost: {} yen", cost);
}

この例では、重さと配送オプションに応じた送料を計算しています。

注意点: 条件の漏れを防ぐ

  • 網羅性の確保: Rustでは、match文がすべてのケースを網羅することが求められます。_を使ったデフォルトケースを活用しましょう。
  • 分岐の整理: 複雑な条件を扱う際には、論理的に分かりやすい順序でパターンを記述することが重要です。

まとめ


複雑な条件の評価は、match文の強力な特徴の1つです。タプルや列挙型、ガード条件を組み合わせることで、柔軟で効率的なコードを書くことができます。適切に構造化することで、可読性を損なわずに複雑なロジックを実現できます。

パターンマッチングと計算の組み合わせ

Rustのmatch文では、パターンマッチングを活用して条件に基づいた計算を行うことができます。パターンマッチングを巧みに組み合わせることで、より効率的かつ簡潔に処理を記述することが可能です。

データ構造を使ったパターンマッチング


以下は、タプルを使って複数の値を同時に評価し、計算を行う例です。

fn calculate_area(shape: (&str, u32)) -> u32 {
    match shape {
        ("circle", radius) => (3.14 * (radius as f64).powi(2)) as u32,
        ("square", side) => side * side,
        ("rectangle", (width, height)) => width * height,
        _ => 0,
    }
}

fn main() {
    let shape1 = ("circle", 10);
    let shape2 = ("square", 5);

    println!("Circle area: {} sq units", calculate_area(shape1));
    println!("Square area: {} sq units", calculate_area(shape2));
}

この例では、タプル内のデータに基づいて計算を行っています。条件ごとに異なる計算式を適用できます。

列挙型を活用した計算


列挙型を使用すると、データの意味を明確にしつつ条件分岐と計算を組み合わせることができます。

enum Shape {
    Circle(u32),
    Square(u32),
    Rectangle(u32, u32),
}

fn calculate_area(shape: Shape) -> u32 {
    match shape {
        Shape::Circle(radius) => (3.14 * (radius as f64).powi(2)) as u32,
        Shape::Square(side) => side * side,
        Shape::Rectangle(width, height) => width * height,
    }
}

fn main() {
    let shape1 = Shape::Circle(10);
    let shape2 = Shape::Rectangle(5, 10);

    println!("Circle area: {} sq units", calculate_area(shape1));
    println!("Rectangle area: {} sq units", calculate_area(shape2));
}

この例では、列挙型を利用して図形の種類とそのプロパティを管理し、それに基づく計算を行っています。

パターンガードを使用した条件の絞り込み


パターンガードを使用すると、条件をさらに絞り込むことができます。

fn classify_number(num: i32) -> &'static str {
    match num {
        n if n > 0 && n % 2 == 0 => "Positive even",
        n if n > 0 => "Positive odd",
        n if n < 0 && n % 2 == 0 => "Negative even",
        n if n < 0 => "Negative odd",
        _ => "Zero",
    }
}

fn main() {
    let number = -4;

    println!("Number classification: {}", classify_number(number));
}

この例では、数値が正か負か、および偶数か奇数かを組み合わせた条件で分類しています。

ネストしたデータ構造のマッチング


ネストしたデータ構造をパターンマッチングで処理することも可能です。

fn analyze_nested_data(data: Option<(i32, i32)>) -> &'static str {
    match data {
        Some((x, y)) if x > 0 && y > 0 => "Both positive",
        Some((x, y)) if x < 0 && y < 0 => "Both negative",
        Some((_, _)) => "Mixed signs",
        None => "No data",
    }
}

fn main() {
    let data = Some((10, -5));

    println!("Analysis result: {}", analyze_nested_data(data));
}

この例では、Option型のネストされたデータ構造に基づき、条件を分岐しています。

計算の抽象化


複雑な計算を別の関数に分離し、match文と組み合わせることでコードを整理できます。

fn calculate_discount(price: u32) -> u32 {
    match price {
        0..=100 => calculate_rate(price, 5),
        101..=500 => calculate_rate(price, 10),
        _ => calculate_rate(price, 20),
    }
}

fn calculate_rate(price: u32, rate: u32) -> u32 {
    price * rate / 100
}

fn main() {
    let price = 200;

    println!("Discount: {} yen", calculate_discount(price));
}

この例では、計算のロジックを別の関数に抽象化し、コードの再利用性と可読性を向上させています。

まとめ


パターンマッチングと計算を組み合わせることで、複雑な条件分岐を効率的に処理できます。タプルや列挙型、パターンガード、ネストしたデータ構造などを組み合わせると、柔軟で直感的なロジックを記述できるようになります。このアプローチを活用することで、Rustコードの簡潔さと効率性を最大限に引き出しましょう。

演習問題: `match`文での式評価

Rustのmatch文をより深く理解するために、演習問題に挑戦しましょう。これらの問題を解くことで、条件分岐、式評価、値の計算におけるmatch文の実用性を体感できます。

問題1: 数値の分類


整数が正、負、またはゼロかを判定し、次のルールでメッセージを出力してください:

  • 正の偶数の場合: "Positive even"
  • 正の奇数の場合: "Positive odd"
  • 負の偶数の場合: "Negative even"
  • 負の奇数の場合: "Negative odd"
  • ゼロの場合: "Zero"

例:
入力: 7
出力: "Positive odd"

解答例:

fn classify_number(num: i32) -> &'static str {
    match num {
        n if n > 0 && n % 2 == 0 => "Positive even",
        n if n > 0 => "Positive odd",
        n if n < 0 && n % 2 == 0 => "Negative even",
        n if n < 0 => "Negative odd",
        _ => "Zero",
    }
}

fn main() {
    let number = 7;
    println!("{}", classify_number(number));
}

問題2: 図形の面積計算


以下のルールに従い、図形の種類とそのパラメータに基づいて面積を計算する関数を作成してください:

  • ("circle", radius) 円の面積: π × 半径²
  • ("square", side) 正方形の面積: 辺²
  • ("rectangle", width, height) 長方形の面積: 幅 × 高さ

例:
入力: ("circle", 10)
出力: 314

解答例:

fn calculate_area(shape: (&str, u32)) -> u32 {
    match shape {
        ("circle", radius) => (3.14 * (radius as f64).powi(2)) as u32,
        ("square", side) => side * side,
        ("rectangle", width, height) => width * height,
        _ => 0,
    }
}

fn main() {
    let shape = ("circle", 10);
    println!("Area: {}", calculate_area(shape));
}

問題3: 成績判定


スコアに基づいて成績を次のルールで判定し、返してください:

  • 90~100: "A"
  • 80~89: "B"
  • 70~79: "C"
  • 60~69: "D"
  • それ以外: "F"

例:
入力: 85
出力: "B"

解答例:

fn determine_grade(score: u32) -> &'static str {
    match score {
        90..=100 => "A",
        80..=89 => "B",
        70..=79 => "C",
        60..=69 => "D",
        _ => "F",
    }
}

fn main() {
    let score = 85;
    println!("Grade: {}", determine_grade(score));
}

問題4: ユーザーのアクセス権


ユーザーの役割とログイン状態に応じてアクセス権を判定する関数を作成してください:

  • ("Admin", true) -> "Full access"
  • ("Moderator", true) -> "Moderate access"
  • ("User", true) -> "Basic access"
  • その他の場合 -> "No access"

例:
入力: ("Admin", true)
出力: "Full access"

解答例:

fn access_rights(role: &str, logged_in: bool) -> &'static str {
    match (role, logged_in) {
        ("Admin", true) => "Full access",
        ("Moderator", true) => "Moderate access",
        ("User", true) => "Basic access",
        _ => "No access",
    }
}

fn main() {
    let role = "Admin";
    let logged_in = true;

    println!("Access rights: {}", access_rights(role, logged_in));
}

まとめ


これらの演習問題を通じて、match文を使った条件分岐や計算ロジックの作成に慣れることができます。実際にコードを実行し、match文の柔軟性とパワーを体感してください。Rustにおけるmatch文の理解を深めることは、効果的なプログラム設計の基礎となります。

まとめ

本記事では、Rustのmatch文を使用した式評価と値の計算方法について、基礎から応用までを解説しました。match文の基本構造、ネストした条件分岐、パターンマッチングを用いた複雑な計算の実装例を通じて、Rust特有の安全で効率的なコードの書き方を学びました。

Rustのmatch文は、柔軟性と強力な型システムを活かして、簡潔でミスの少ない条件分岐を実現できます。特に、値を直接計算して返せる特徴を活用することで、コードの可読性と効率性が大幅に向上します。今後のプログラム作成において、この記事で学んだmatch文の使い方を活用し、より堅牢で効率的なRustコードを構築してください。

コメント

コメントする

目次