Rust: Rust Flow Control

Last updated: 2026-08-26

Rust's match is not a switch—it's more powerful because the compiler checks to make sure you've covered all cases.

Control flow is the backbone of a program. In addition to traditional if/else, Rust has two major strengths: match pattern matching and if let syntactic sugar.


1. What You'll Learn



2. The Story of the Rock-Paper-Scissors Referee

(1) The Struggle: Writing a Rock-Paper-Scissors Decision Logic Using if-else Statements

Emma is a game developer who wants to add a Rock-Paper-Scissors feature to her game:

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

Code that uses if/else for determination:

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

Writing a whole bunch of if/else makes it easy to miss certain combinations, and it's hard to read.

(2) An Elegant Solution for Rust's match Statement

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 It's like a referee here: it maps the player's and the computer's punch combinations one-to-one, with no omissions and no ambiguity. The compiler checks to see if you've written all 9 combinations.



3. if/else Conditional Branching

(1) Basic Usage

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 the if is an expression—it can return a value

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

Important: The two branches of if must be of the same type. if true { 5 } else { "six" } will result in a compilation error.



4. match Pattern Matching

(1) Basic Syntax

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) Key Rules for "match"

Rule Description
Exhaustiveness Must cover all possible values; omitting any branch will result in a compilation error
Wildcard _ Matches all remaining cases; placed at the end
Multi-mode | One branch matches multiple patterns: 1 | 2 =>
Range Matching 1..=5 Matches the closed interval from 1 to 5
Guard Additional conditions: x if x > 5 =>

(3) Comparing if and match

Characteristics if/else match
Matching Method Boolean Condition Pattern Matching
Exhaustive Check None (else can be omitted) Mandatory (must cover all values)
Deconstruction Capabilities None Supports deconstruction of enumerations, tuples, and structs
Multi-value matching Requires `
Use Cases Range Checks, Boolean Logic Enumeration Processing, Value Classification, Deconstruction


5. if let Syntactic Sugar

When you're only interested in a specific pattern and not in other values, if let is more concise than match:

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) Quick Reference for Control Flow Keywords

Keyword/Syntax Purpose Example
if / else if / else Conditional Branch if x > 0 { ... } else { ... }
match Pattern Matching match x { 1 => ..., _ => ... }
if let Single-mode matching syntactic sugar if let Some(v) = x { ... }
while Conditional Loops while x > 0 { ... }
while let Pattern-Matching Loop while let Some(v) = iter.next() { ... }
loop Infinite Loop loop { ... break; }
Iterator traversal
break Exit Loop break; or break value;
continue Skip this iteration continue;


6. Complete Example

▶ Example 1: if/else Grade Classification (Difficulty ⭐)

Output:

TEXT 📖 Display only
Fractions: 88, Level: <grade>
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);
}

Output:

TEXT 📖 Display only
Fractions: 88, Level: Good (B)

Output:

TEXT 📖 Display only
Status Code <code>: <handle_status_code(code)>

if Each branch of the expression must return the same type. Here, all branches return &str, so the compiler is happy.


▶ Example 2: Using match to handle HTTP status codes (Difficulty: ⭐⭐)

Output:

TEXT 📖 Display only
Status Code <code>: <handle_status_code(code)>
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));
    }
}

Output:

TEXT 📖 Display only
Fractions: 88, Level: Good (B)

Output:

TEXT 📖 Display only
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

| The operator allows a branch to match multiple patterns, avoiding duplicate code. _ Wildcards ensure that all status codes are covered.


▶ Example 3: match guards and if let (Difficulty ⭐⭐)

Output:

TEXT 📖 Display only
Very Large Numbers: <x>
Big Numbers: <x>
Small numbers: <x>
No numbers
Language is: <lang>
Not null: <x>
No value
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");
    }
}

Output:

TEXT 📖 Display only
Small numbers: 42
Language is: Rust
No value

Use the if keyword in a match statement to add an additional condition after the pattern—this branch will only be entered if the pattern matches and the condition is true. if let is suitable for scenarios where you are "only concerned with one pattern."


▶ Example 4: Using match to deconstruct enums and tuples (Difficulty: ⭐⭐⭐)

Output:

TEXT 📖 Display only
=== Graphical Description ===
<describe_shape(shape)>

=== Coordinate Classification ===
Point <point>: <desc>
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);
    }
}

Output:

TEXT 📖 Display only
=== 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)

The destructuring capabilities of match allow you to simultaneously match enum variants, extract inner values, and apply guard conditions to implement precise classification logic. The compiler's exhaustive checking ensures that you won't overlook any cases.


▶ Example 5: Practical Application of if let and while let (Difficulty: ⭐⭐)

Output:

TEXT 📖 Display only
=== if let Processing Option ===
Division Successful: <value>
Division Failed: Divisor is zero

=== if let Chain Processing ===
Alice's number: <num>
Bob's number: <num>
Bob has no data input

=== while let Simulation Iteration ===
Popped: <top>
The stack is empty
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");
}

Output:

TEXT 📖 Display only
=== 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 is suitable for scenarios where you are only concerned with Some/Ok and can ignore other cases; while let is suitable for scenarios where you iterate through values until you encounter None (such as pop() stack operations). Both are more concise than the full match.


❓ FAQ

Q What is the difference between match and switch?
A match is much more powerful than switch.
Q Why is exhaustive match checking so important?
A Because it's "the compiler helping you find bugs."
Q When should you use if let?
A When you're only interested in one pattern.
Q What is the difference between the _ wildcard and a variable name in match?
A The _ completely ignores the value and does not bind it, while a variable name binds the value but may trigger a compiler warning for unused variables.
Q What should I do if the two branches of an if expression are of different types?
A A compilation error will occur.

📖 Summary


📝 Exercises

  1. Difficulty ⭐: Write a function fn number_to_word(n: i32) -> &'static str that uses match to map the digits 0–5 to their corresponding English words (zero/one/two/three/four/five) and returns "unknown" for all other digits.
  2. Difficulty ⭐⭐: Define an enum enum TrafficLight { Red, Yellow, Green } and use match to return the corresponding "wait time" for each color (red: 30s, yellow: 3s, green: 45s).
  3. Difficulty ⭐⭐⭐ : Write a function fn describe_point(point: (i32, i32)) that uses match to process a two-dimensional coordinate point: if the point is at the origin (0,0), output "origin"; if it is on the x-axis (x,0), output "on x-axis"; if it is on the y-axis (0,y), output "on y-axis"; for all other cases, output the coordinate values.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏