Control Flow
Rust provides if/else, three loop forms (loop, while, for), and exhaustive match. Loops and branches can produce values when used as expressions.
Search across all documentation pages
Rust provides if/else, three loop forms (loop, while, for), and exhaustive match. Loops and branches can produce values when used as expressions.
fn sum_evens(nums: &[i32]) -> i32 {
let mut total = 0;
for &n in nums {
if n % 2 == 0 {
total += n;
}
}
total
}
fn first_negative(nums: &[i32]) -> Option<i32> {
for &n in nums {
if n < 0 {
return Some(n);
}
}
None
}When to reach for this: Any branching or iteration. Prefer for over while with manual index when iterating collections.
fn find_pair(target: i32, nums: &[i32]) -> Option<(usize, usize)> {
'outer: for i in 0..nums.len() {
for j in (i + 1)..nums.len() {
if nums[i] + nums[j] == target {
break 'outer (i, j);
}
}
}
}
fn retry<F: FnMut() -> bool>(mut f: F, max: u32) -> bool {
let mut attempts = 0;
loop {
if f() {
break true;
}
attempts += 1;
if attempts >= max {
break false;
}
}
}
fn main() {
let nums = [2, 7, 11, 15];
println!("{:?}", find_pair(9, &nums));
println!("{}", retry(|| false, 3));
}What this demonstrates:
break 'outer exits nested loops with a tuple valueloop with conditional break implements retry logic0..nums.len() for index-based access when neededOption for absent results instead of sentinel values| Construct | Use |
|---|---|
for x in iter | Iterate collections, ranges, adapters |
while cond | Loop until condition is false |
loop | Infinite loop until break (can return value) |
let squares: Vec<i32> = (0..5).map(|n| n * n).collect();match Exhaustivenessenum Color { Red, Green, Blue }
fn hex(c: Color) -> &'static str {
match c {
Color::Red => "#f00",
Color::Green => "#0f0",
Color::Blue => "#00f",
}
}Compiler errors if a variant is missing. Use _ only when you intentionally catch remaining cases.
if let and while letlet Some(x) = opt else { return };
while let Some(line) = lines.next() {
println!("{line}");
}Ergonomic partial matching without full match.
if condition not bool - if 1 { } fails. Fix: Use explicit comparisons.0..n excludes n. Fix: Use 0..=n for inclusive end when needed.for loops - for item in &vec borrows; for item in vec moves. Fix: Choose iter(), iter_mut(), or into_iter() deliberately.match - Adding enum variants breaks compiles. Fix: Treat as a feature; update all matches.break value type mismatch - All break in a loop must return the same type. Fix: Annotate or unify types.| Alternative | Use When | Don't Use When |
|---|---|---|
| Iterator adapters | Transform/filter without explicit loop | Side effects need sequential clarity |
if let chain | Single-pattern checks | Many variants need match |
matches! macro | Boolean predicate on pattern | You need bound values |
| Recursion | Tree/graph structure | Deep stacks risk overflow |
Yes. for x in &vec borrows elements. for x in &mut vec mutably borrows.
for (i, v) in items.iter().enumerate() is idiomatic. Raw 0..len works when neighbors are needed.
In loop, break 42 makes the loop expression evaluate to 42.
No. Only loop supports break value. Use loop or assign inside while.
Skips to the next iteration of the labeled loop, like labeled break.
match x { n if n > 0 => ..., _ => ... } adds a boolean condition to a pattern.
No. Use match for exhaustive pattern matching.
Yes for integers and chars: 1..=5, 'a'..='z'.
Not in Rust. Use break flags or Option return values instead.
for (k, v) in &map or map.iter(). Order is arbitrary unless you use BTreeMap.
match patternsStack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Reviewed by Chris St. John·Last updated Jul 16, 2026