LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 814 min read

Control Flow

Use if/else as an expression, iterate with for and ranges, and understand the three loop constructs — loop, while, and for — including loop's ability to return a value.

Introduction

Rust's control flow will feel familiar if you've used any C-family language, with two notable twists: `if` is an expression that can produce a value, and Rust deliberately has no traditional C-style `for (;;)` loop, favoring safer alternatives instead.

This lesson covers if/else as an expression, the for loop with ranges, while loops, the standalone loop construct with break value, and loop labels for controlling nested loops.

What You Will Learn
  • How if/else works, and how it can be used as an expression that produces a value.
  • How to iterate with for over ranges and collections.
  • How while loops work.
  • How loop repeats forever until break, and how break can return a value.
  • How loop labels let you break or continue an outer loop from inside a nested one.

if/else as an Expression

`if` in Rust doesn't need parentheses around the condition, and — unlike many languages — it is an expression, not just a statement. That means you can assign the result of an if/else directly to a variable, as long as every branch produces the same type. This is why Rust has no separate ternary operator: if/else already covers that case.

The for Loop and Ranges

The `for` loop is the idiomatic way to iterate a known number of times or over a collection. `1..=3` is an inclusive range (1, 2, 3), while `1..3` is a half-open range (1, 2). `for item in &collection` iterates references to each element without taking ownership of the collection.

The while Loop

`while condition { ... }` repeats its body for as long as the condition evaluates to true, re-checking the condition before every iteration. It's the right tool when the number of iterations isn't known ahead of time and depends on some changing state.

loop and break with a Value

`loop` repeats its body forever until an explicit `break`. Unlike `while` or `for`, `loop` can return a value: writing `break some_value;` ends the loop and makes that value the result of the whole loop expression, which you can assign directly to a variable.

Loop Labels

When loops are nested, a plain `break` or `continue` only affects the innermost loop. Labeling an outer loop with a name like `'outer: loop { ... }` lets you write `break 'outer;` or `continue 'outer;` from inside a nested loop to control the labeled one specifically.

Code Example

fn main() {
let number = 7;
if number % 2 == 0 {
println!("{} is even", number);
} else {
println!("{} is odd", number);
}
// if is an expression, so it can produce a value
let description = if number > 5 { "big" } else { "small" };
println!("{} is {}", number, description);
for i in 1..=3 {
println!("for loop: {}", i);
}
let mut count = 0;
let result = loop {
count += 1;
if count == 5 {
break count * 2;
}
};
println!("loop result: {}", result);
}
Output

Click Run to see what this code prints.

Common Mistakes

Avoid These Mistakes
  • Writing a manual index-based while loop (`while i < v.len()`) instead of the safer, more idiomatic `for item in &v`, risking off-by-one and out-of-bounds bugs.
  • Forgetting that `loop` alone never ends without an explicit `break`.
  • Mismatching the types returned by the two branches of an if/else used as an expression, which is a compile error since both branches must produce the same type.
  • Forgetting the `=` in inclusive ranges (`1..=3`) and getting one fewer iteration than expected with `1..3`.

Best Practices

  • Prefer `for item in &collection` over manual indexing whenever iterating a collection.
  • Use `loop` with `break value` when you need to compute something before exiting a loop.
  • Use labeled loops when you need break or continue to target an outer loop from inside a nested one.
  • Keep if/else expressions used as values simple enough that reading them stays clear.

Frequently Asked Questions

No — Rust deliberately omits it in favor of for item in range/collection and the standalone loop construct, which cover the same needs more safely.

Yes, using loop labels like 'outer: loop { ... break 'outer; }.

1..5 is a half-open range (1, 2, 3, 4) and 1..=5 is inclusive (1, 2, 3, 4, 5).

Yes — match, covered in the previous lesson, is Rust's primary branching construct for enums and value patterns, alongside if/else and loops.

Key Takeaways

  • if/else in Rust is an expression and can produce a value, as long as all branches share the same type.
  • for item in range/collection is the idiomatic, safe way to iterate.
  • while repeats based on a condition; loop repeats forever until an explicit break, which can return a value.
  • Loop labels let break and continue target a specific outer loop when loops are nested.

Summary

You now know how to branch and iterate idiomatically in Rust, including using if and loop as expressions that produce values. Next, you'll take a closer look at functions — parameters, return types, and the expression-based return style you've already been seeing throughout this course.

Next Lesson →

Functions