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…