404 Not Found

404 Not Found


nginx

Rustのフロー制御:if-else、match、if letの完全ガイド

Rustのmatchswitchとは異なります。コンパイラがすべてのケースを網羅しているかどうかを確認するため、より強力なのです。

制御フローはプログラムの根幹をなすものです。従来の if/else に加え、Rust には match パターンマッチングif let シンタックスシュガー という 2 つの大きな強みがあります。


1. 学習内容


2. 「じゃんけん」の審判の物語

(1) 試行錯誤:if-else文を使って「じゃんけん」の判定ロジックを書く

エマはゲーム開発者で、自分のゲームにじゃんけんの機能を追加したいと考えています:

TEXT
Player's turn: Rock (1), Scissors (2), Paper (3)
Computer: Pick one at random
Needs to be determined: Win, Lose, or Tie

判定に if/else を使用するコード:

TEXT
if Player == 1 && Computer == 1 → Tie
else if Player == 1 && Computer == 2 → Win
else if Player == 1 && Computer == 3 → Lose
... Still needs 6 branches!

if/elseを大量に書き連ねると、特定の組み合わせを見落としやすくなり、読みづらくなってしまいます。

(2) Rustのmatch文に対する洗練された解決策

RUST
fn main() {
    let player = 1;    // Rock
    let computer = 2;  // Scissors

    let result = match (player, computer) {
        (1, 1) | (2, 2) | (3, 3) => "Tie",
        (1, 2) | (2, 3) | (3, 1) => "You Win!",
        (1, 3) | (2, 1) | (3, 2) => "You Lose!",
        _ => "Invalid Punch",
    };

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

match ここはまるで審判のようなものです。プレイヤーとコンピュータのパンチの組み合わせを、漏れもなく曖昧さもなく、1対1で対応させています。コンパイラは、9つの組み合わせをすべて記述しているかどうかをチェックします。


3. if/else による条件分岐

(1) 基本的な使い方

RUST
fn main() {
    let number = 7;

    if number < 5 {
        println!("A number less than 5");
    } else if number == 5 {
        println!("The number equals 5");
    } else {
        println!("The number is greater than 5");
    }
}

(2) if が式である場合、値を返すことができる

RUST
let condition = true;
let value = if condition { 5 } else { 6 };
// Note:The two branches must be of the same type.
println!("value = {}", value);  // value = 5

重要if の 2 つの分岐は、同じ型でなければなりません。if true { 5 } else { "six" } を使用すると、コンパイルエラーが発生します。


4. パターンマッチング

(1) 基本的な構文

100%
graph TB
    A[match Expressions] --> B[Each branch = A Pattern]
    B --> C[Pattern match successful → Run this branch]
    B --> D[Pattern Mismatch → Try the next branch]
    A --> E[Finally, there must be _ Wildcard Fallback]
RUST
let number = 3;
match number {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("other"),  // Wildcard——Match all remaining cases
}

(2) 「match」の主なルール

ルール 説明
網羅性 考えられるすべての値を網羅しなければならない。いずれかの分岐を省略すると、コンパイルエラーが発生する
ワイルドカード _ 残りのすべてのケースに一致する。末尾に配置される
マルチモード | 1つの分岐が複数のパターンに一致する:1 | 2 =>
範囲の一致 1..=5 1 から 5 までの閉区間と一致する
ガード 追加条件:x if x > 5 =>

(3) ifmatch の比較

特徴 if/else match
照合方法 ブール条件 パターン照合
網羅的なチェック なし(この場合は省略可) 必須(すべての値を網羅する必要がある)
分解機能 なし 列挙型、タプル、構造体の分解に対応
複数値の一致 `
ユースケース 範囲チェック、ブール論理 列挙処理、値の分類、分解

5. if let の構文上の簡略化

特定のパターンのみに興味があり、他の値には関心がない場合、if letmatch よりも簡潔です:

RUST
let optional = Some(5);

// match Writing Style (verbose)
match optional {
    Some(value) => println!("The value is: {}", value),
    _ => (),  // You must write this empty branch.
}

// if let Writing Style (Concise)
if let Some(value) = optional {
    println!("The value is: {}", value);
    // No need to write _ => ()
}

(4) 制御フローキーワードのクイックリファレンス

キーワード/構文 目的
if / else if / else 条件分岐 if x > 0 { ... } else { ... }
match パターンマッチング match x { 1 => ..., _ => ... }
if let シングルモードのマッチングを表す構文糖衣 if let Some(v) = x { ... }
while 条件分岐 while x > 0 { ... }
while let パターンマッチングループ while let Some(v) = iter.next() { ... }
loop Infinite Loop loop { ... break; }
イテレータの走査
break ループ終了 break; または break value;
continue この反復をスキップ continue;

6. 完全な例

(1) ▶ サンプル:if/else による成績分類(難易度 ⭐)

RUST
// ============================================
// Use if expression to grade scores and return a string
// ============================================

fn main() {
    let score = 88;

    let grade = if score >= 90 {
        "Excellent (A)"
    } else if score >= 80 {
        "Good (B)"
    } else if score >= 70 {
        "Intermediate (C)"
    } else if score >= 60 {
        "Passing Grade (D)"
    } else {
        "Fail (F)"
    };

    println!("Fractions: {}, Level: {}", score, grade);
}

出力:

TEXT
Fractions: 88, Level: Good (B)

if 式の各分岐は、同じ型を返さなければなりません。ここでは、すべての分岐が &str を返すため、コンパイラは問題ないと判断します。


(2) ▶ サンプル:match を使用した HTTP ステータスコードの処理 (難易度: ⭐⭐)

RUST
// ============================================
// Use match to gracefully handle HTTP Status Codes
// ============================================

fn handle_status_code(code: u16) -> &'static str {
    match code {
        200 => "OK: Request Successful",
        201 => "Created: The resource has been created",
        204 => "No Content: No results found",
        301 | 302 => "Redirect: The resource has been moved",
        400 => "Bad Request: Invalid request format",
        401 => "Unauthorized: Unauthorized",
        403 => "Forbidden: Access Denied",
        404 => "Not Found: Resource does not exist",
        500 => "Internal Server Error: Internal Server Error",
        502 | 503 => "Server Error: The server is temporarily unavailable",
        _ => "Unknown: Unknown status code",
    }
}

fn main() {
    let codes = [200, 404, 418, 500];
    for &code in codes.iter() {
        println!("Status Code {}: {}", code, handle_status_code(code));
    }
}

出力:

TEXT
Status Code 200: OK: Request Successful
Status Code 404: Not Found: Resource does not exist
Status Code 418: Unknown: Unknown status code
Status Code 500: Internal Server Error: Internal Server Error

| この演算子を使用すると、1つの分岐で複数のパターンに一致させることができ、コードの重複を回避できます。_ ワイルドカードを使用することで、すべてのステータスコードを確実に網羅できます。


(3) ▶ サンプル:マッチガードと if let (難易度 ⭐⭐)

RUST
// ============================================
// match Guard + if let Simplified Pattern Matching
// ============================================

fn main() {
    let number = Some(42);

    // match Guard: Add additional conditions to the pattern
    match number {
        Some(x) if x > 100 => println!("Very Large Numbers: {}", x),
        Some(x) if x > 50  => println!("Big Numbers: {}", x),
        Some(x)             => println!("Small numbers: {}", x),
        None                => println!("No numbers"),
    }

    // if let Syntactic sugar -- Only care about the Some case
    let value = Some("Rust");
    if let Some(lang) = value {
        println!("Language is: {}", lang);
    }

    // if let with else
    let empty: Option<i32> = None;
    if let Some(x) = empty {
        println!("Not null: {}", x);
    } else {
        println!("No value");
    }
}

出力:

TEXT
Small numbers: 42
Language is: Rust
No value

match文でifキーワードを使用すると、パターンの後に追加の条件を指定できます。この分岐は、パターンが一致し、かつ条件が真の場合にのみ実行されます。if letは、「1つのパターンのみを対象とする」ような場合に適しています。


(4) ▶ サンプル:match を使った列挙型とタプルの分解 (難易度: ⭐⭐⭐)

RUST
// ============================================
// match Deconstruction: Enumeration + Tuple + Guard
// ============================================

enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Triangle(f64, f64, f64),
}

fn describe_shape(shape: &Shape) -> String {
    match shape {
        Shape::Circle(radius) if *radius > 10.0 => {
            format!("Big Circle (Radius {:.1})", radius)
        }
        Shape::Circle(radius) => {
            format!("Small Circle (Radius {:.1})", radius)
        }
        Shape::Rectangle(w, h) if w == h => {
            format!("Square (Side length {:.1})", w)
        }
        Shape::Rectangle(w, h) => {
            format!("Rectangle (Width {:.1}, Height {:.1})", w, h)
        }
        Shape::Triangle(a, b, c) => {
            format!("Triangle (Sides {:.1}, {:.1}, {:.1})", a, b, c)
        }
    }
}

fn main() {
    let shapes = [
        Shape::Circle(5.0),
        Shape::Circle(15.0),
        Shape::Rectangle(4.0, 4.0),
        Shape::Rectangle(3.0, 5.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    println!("=== Graphical Description ===");
    for shape in &shapes {
        println!("{}", describe_shape(shape));
    }

    println!("\n=== Coordinate Classification ===");
    let points: [(i32, i32); 4] = [(0, 0), (3, 0), (0, -2), (5, 7)];
    for point in points {
        let desc = match point {
            (0, 0) => "Origin".to_string(),
            (x, 0) => format!("On x-axis (x={})", x),
            (0, y) => format!("On y-axis (y={})", y),
            (x, y) if x > 0 && y > 0 => format!("First Quadrant ({}, {})", x, y),
            (x, y) => format!("Other Quadrants ({}, {})", x, y),
        };
        println!("Point {:?}: {}", point, desc);
    }
}

出力:

TEXT
=== Graphical Description ===
Small Circle (Radius 5.0)
Big Circle (Radius 15.0)
Square (Side length 4.0)
Rectangle (Width 3.0, Height 5.0)
Triangle (Sides 3.0, 4.0, 5.0)

=== Coordinate Classification ===
Point (0, 0): Origin
Point (3, 0): On x-axis (x=3)
Point (0, -2): On y-axis (y=-2)
Point (5, 7): First Quadrant (5, 7)

matchのデストラクチャリング機能を使えば、列挙型のバリアントを同時にマッチングさせ、内部値を抽出するとともに、ガード条件を適用して、正確な分類ロジックを実装することができます。コンパイラによる網羅的なチェックにより、いかなるケースも見落とすことがありません。


(5) ▶ サンプル:if letwhile let の実践的な応用(難易度:⭐⭐)

RUST
// ============================================
// if let and while let: Simplify Option/Result Processing
// ============================================

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    println!("=== if let Processing Option ===");
    let results = [divide(10.0, 2.0), divide(8.0, 0.0), divide(15.0, 3.0)];

    for result in &results {
        if let Some(value) = result {
            println!("Division Successful: {:.2}", value);
        } else {
            println!("Division Failed: Divisor is zero");
        }
    }

    println!("\n=== if let Chain Processing ===");
    let alice_input: Option<&str> = Some("42");
    let bob_input: Option<&str> = None;

        if let Some(num_str) = alice_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Alice's number: {}", num);
        }
    }

    if let Some(num_str) = bob_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Bob's number: {}", num);
        }
    } else {
        println!("Bob has no data input");
    }

    println!("\n=== while let Simulation Iteration ===");
    let mut stack = vec![3, 2, 1];
    while let Some(top) = stack.pop() {
        println!("Popped: {}", top);
    }
    println!("The stack is empty");
}

出力:

TEXT
=== if let Processing Option ===
Division Successful: 5.00
Division Failed: Divisor is zero
Division Successful: 5.00

=== if let Chain Processing ===
Alice's number: 42
Bob has no data input

=== while let Simulation Iteration ===
Popped: 1
Popped: 2
Popped: 3
The stack is empty

if letは、Some/Okのみを扱い、その他のケースを無視できるシナリオに適しています。while letは、Noneに遭遇するまで値を反復処理するシナリオ(pop()のスタック操作など)に適しています。どちらも、完全な match よりも簡潔です。


❓ よくある質問

Q matchswitch の違いは何ですか?
A matchswitch よりもはるかに強力です。
Q なぜ網羅的な match チェックがそれほど重要なのでしょうか?
A それは「コンパイラがバグの発見を手助けしてくれる」からです。
Q if let はどのような場合に使用すべきですか?
A 1つのパターンのみを対象とする場合です。
Q match におけるワイルドカード「_」と変数名の違いは何ですか?
A 「_」は値を完全に無視してバインドしませんが、変数名は値をバインドします。ただし、未使用の変数に対してコンパイラから警告が出る場合があります。
Q if 式の 2 つの分岐の型が異なる場合はどうすればよいですか?
A コンパイルエラーが発生します。

📖 まとめ


📝 練習問題

  1. 難易度 ⭐: match を使用して、数字 0~5 を対応する英語の単語(zero/one/two/three/four/five)にマッピングし、それ以外の数字に対してはすべて「unknown」を返す関数 fn number_to_word(n: i32) -> &'static str を記述してください。
  2. 難易度 ⭐⭐: 列挙型 enum TrafficLight { Red, Yellow, Green } を定義し、match を使用して各色に対応する「待機時間」を返す(赤:30秒、黄:3秒、緑:45秒)。
  3. 難易度 ⭐⭐⭐ :関数 fn describe_point(point: (i32, i32)) を記述し、match を使用して 2 次元座標上の点を処理するようにします。その点が原点 (0,0) にある場合は「origin」を出力し、 その点が x 軸上にある場合は (x,0)、「on x-axis」を出力し、y 軸上にある場合は (0,y)、「on y-axis」を出力する。それ以外の場合は、座標値を出力する。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%