iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

if / match

Rust control flow: if expressions, match, loop, while, for. Everything is an expression with a value.

Rust — control flow

EXAMPLE
// ===== if is an expression =====
fn classify(n: i32) -> &'static str {
    if n < 0 { "negative" }
    else if n == 0 { "zero" }
    else { "positive" }
}

let parity = if n % 2 == 0 { "even" } else { "odd" };
// Both arms must produce the same type.

// ===== match (exhaustive) =====
fn describe(n: i32) -> &'static str {
    match n {
        0 => "zero",
        1..=9 => "small",
        10..=99 => "medium",
        _ => "big",
    }
}

// Pattern matching:
match opt {
    Some(value) => println!("got {value}"),
    None => println!("empty"),
}

// Tuple, struct, enum patterns:
match point {
    Point { x: 0, y: 0 } => println!("origin"),
    Point { x, y: 0 } => println!("on x-axis at {x}"),
    Point { x: 0, y } => println!("on y-axis at {y}"),
    Point { x, y } => println!("({x},{y})"),
}

// Guard:
match n {
    x if x < 0 => println!("negative"),
    _ => println!("non-negative"),
}

// ===== if let / while let =====
if let Some(v) = opt { println!("{v}"); }
while let Some(top) = stack.pop() { println!("{top}"); }

// let-else (Rust 1.65+):
let Some(v) = opt else { return; };
// v is now in scope; if None, early return.

// ===== loop / break with value =====
let answer = loop {
    if ready { break 42; }
};

// Labeled loops:
'outer: for i in 0..10 {
    for j in 0..10 {
        if i * j > 50 { break 'outer; }
    }
}

// ===== while =====
while n > 0 {
    n -= 1;
}

// ===== for over iterators =====
for x in 1..=10 { println!("{x}"); }      // inclusive end
for x in [1, 2, 3] { println!("{x}"); }
for (i, x) in v.iter().enumerate() { println!("{i}: {x}"); }

// Range types: 0..10 (exclusive), 0..=10 (inclusive)

// ===== ? operator (early return on error) =====
fn read_config() -> Result<Config, std::io::Error> {
    let text = std::fs::read_to_string("config.toml")?;   // returns Err if read fails
    let cfg = parse(&text)?;
    Ok(cfg)
}

// ===== Patterns to internalise =====
// - if / match are expressions; use them for assignment
// - match is exhaustive; let the compiler enforce
// - let-else for 'extract or bail'
// - ? for error propagation
// - Range syntax: ..=10 inclusive, ..10 exclusive

// ===== Pitfalls =====
// - match arm types must agree (compile error otherwise)
// - Missing _ arm in non-exhaustive enum -> compile error
// - Mutating loop variables that are immutable bindings
// - Using ? in a function that does not return Result/Option

Why it matters

Rust control flow is expression-oriented and exhaustive. if returns a value, match must cover every case, loop can return through break, let-else gives early exits, and ? propagates errors cleanly. Reach for match before chains of if-let, and the code reads tighter.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
let n = 7;
let kind = if n % 2 == 0 { "even" } else { "odd" };

match n {
    0 => println!("zero"),
    1..=9 => println!("digit"),
    _ => println!("big"),
}
Try it Yourself »

Discussion

Loading…