← Back to Rust series
🦀
Basics
Basics · Prerequisite: lesson 03 · Basics · Prerequisite: lesson 04

04–05. Control Flow · Functions, Expressions, Return Values, Doc Comments

Rust's `if` is an expression — you can write `let x = if cond { 1 } else { 2 }`. This lesson covers `loop` / `while` / `for`, Rust-specific touches like attaching a value to `break`, labeled break / continue across nested loops, and `for` over ranges and iterators. Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.

Rustcontrol flowifloopwhileforrangefunctionsfnexpressionreturndocumentationcargo doc
Duration
⏱ ~1 hour + ~1 hour
Level
📊 Beginner
Prerequisite
🎯 Lesson 03
OUTCOME
Rust's `if` is an expression — you can write `let x = if cond { 1 } else { 2 }`. This lesson covers `loop` / `while` / `for`, Rust-specific touches like attaching a value to `break`, labeled break / continue across nested loops, and `for` over ranges and iterators. / Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.

What you'll learn

  • 1Understand `if` is an expression and use it as a value
  • 2Pick the right one between `loop` / `while` / `for`
  • 3Return a value from `loop` via `break value`
  • 4Break out of nested loops cleanly with labels
  • 5Iterate with ranges (`0..10`, `0..=10`) and iterators
  • 6Define functions with explicit parameter and return types
  • 7Tell expressions from statements
  • 8Return implicitly from the last expression — no explicit `return`
  • 9Write `///` doc comments and build HTML with `cargo doc`
  • 10Return multiple values via tuples

04. Control Flow (if · loop · while · for · break-with-value)

Rust's `if` is an expression — you can write `let x = if cond { 1 } else { 2 }`. This lesson covers `loop` / `while` / `for`, Rust-specific touches like attaching a value to `break`, labeled break / continue across nested loops, and `for` over ranges and iterators.

Overview

Rust has no `cond ? a : b` ternary. It doesn't need one — `if` itself produces a value, so the same expressiveness comes naturally. This consistency keeps code short and readable.

Core Concepts

1) if is an expression

rust
let n = 10;
let kind = if n % 2 == 0 { "even" } else { "odd" };
println!("{}", kind); // even

Each branch must produce the same type. One arm returning `"even"` and the other `42` is a compile error.

2) loop — infinite, exits via break

  • Unconditional infinite loop
  • `break value` makes that value the result of the `loop` expression
  • Labels (`'outer:`) target which loop break/continue applies to

3) while — runs while condition is true

Skipped entirely if the condition starts false. For index-based traversal `for` is usually nicer.

4) for — iterator traversal

  • `for x in 0..10` — 0,1,...,9 (exclusive upper)
  • `for x in 0..=10` — 0,1,...,10 (inclusive)
  • `for x in &v` — iterate Vec / slice

Hands-on Examples

Returning a value from `loop` — handy for search:

rust
fn main() {
    let arr = [3, 7, 11, 13, 17];
    let target = 11;
    let idx = loop {
        let mut i = 0;
        while i < arr.len() {
            if arr[i] == target { break i as i32; }
            i += 1;
        }
        break -1;
    };
    println!("index = {}", idx); // 2
}

Labels exit nested loops:

rust
fn main() {
    'outer: for i in 0..5 {
        for j in 0..5 {
            if i + j == 6 {
                println!("found ({}, {})", i, j);
                break 'outer;
            }
        }
    }
}

Ranges and iterators with `for`:

rust
fn main() {
    for i in 0..3 { println!("i={}", i); }       // 0,1,2
    for i in 1..=3 { println!("j={}", i); }      // 1,2,3
    let v = vec![10, 20, 30];
    for x in &v { println!("x={}", x); }
    for (idx, x) in v.iter().enumerate() {
        println!("{}-{}", idx, x);
    }
}

Common Mistakes

Q. Where's C-style `for (i = 0; i < n; i++)`?

A. Rust doesn't have it. `for i in 0..n` covers the common case, and `.iter().enumerate()` covers the rest. Off-by-one bugs go down too.

Q. What if `if` arms have different types?

A. Compile error. `let x = if c { 1 } else { "two" }` won't fly. Make types match, or wrap them in an enum.

Q. What's `while let`?

A. A `while` with pattern matching. `while let Some(v) = iter.next() { ... }` loops while the iterator yields Some. Covered in the pattern-matching lesson.

Recap

  • `if` is an expression — use it on the right-hand side of `let`
  • `loop` is infinite, `break value` returns from it
  • `for in range` / `for in iterator` are the most common loop forms
  • Use labels to break out of nested loops precisely

Try It Yourself

  1. Sum every even number from 1 to 100 using `for` and `if`
  2. Use `loop + break value` to compute sqrt(2) by Newton's method in 5 iterations
  3. Find the first `(i, j)` in a 2D grid where `i + j == 7` using a labeled break

05. Functions, Expressions, Return Values, Doc Comments

Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.

Overview

Rust functions are straightforward, but expression-orientation trips up people from imperative languages — **whether you put a semicolon on the last line decides whether anything is returned**. Once that clicks, code gets dramatically shorter.

Core Concepts

1) Function syntax

rust
fn add(a: i32, b: i32) -> i32 {
    a + b   // no semicolon → this is the return value
}

2) Expression vs. statement

  • **Expression** — produces a value. `5`, `a + b`, `if cond { 1 } else { 2 }`, blocks `{ let x = 1; x + 1 }`
  • **Statement** — performs an action but produces no value. `let x = 5;`, `fn f() {}`
  • Adding a semicolon to an expression turns it into a statement — value is discarded

3) Explicit return

Use `return value;` when you want an early exit. For the normal happy path the trailing-expression form is more idiomatic.

4) Doc comments

  • `///` declares documentation for the next item
  • Markdown is supported — code blocks become automatic doctests
  • `cargo doc --open` generates and opens HTML docs

Hands-on Examples

Implicit return alongside explicit `return`:

rust
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        return -1;     // early exit
    }
    a / b             // last expression → returned
}

fn main() {
    println!("{}", divide(10, 3)); // 3
    println!("{}", divide(10, 0)); // -1
}

Return multiple values via a tuple:

rust
fn minmax(v: &[i32]) -> (i32, i32) {
    let mut mn = v[0];
    let mut mx = v[0];
    for &x in v {
        if x < mn { mn = x; }
        if x > mx { mx = x; }
    }
    (mn, mx)
}

fn main() {
    let (a, b) = minmax(&[3, 7, 1, 9, 4]);
    println!("min={}, max={}", a, b); // 1, 9
}

Doc comments — Markdown + doctest:

rust
/// Add two numbers and return the sum.
///
/// # Examples
///
/// ```
/// assert_eq!(my_crate::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
bash
cargo doc --open       # generate and open HTML docs
cargo test --doc        # run doctests

Common Mistakes

Q. My return value disappeared — I wrote `a + b` and got nothing back

A. `a + b;` (with semicolon) is a statement, the value is dropped. The trailing expression must have **no semicolon**.

Q. What's the unit type `()`?

A. Roughly equivalent to `void` elsewhere. Omitting the return type means `-> ()` — the function returns no meaningful value.

Q. Can main return a value?

A. Yes — `fn main() -> Result<(), Box<dyn Error>>` enables `?` inside main. Covered in the error-handling lessons.

Recap

  • Function signatures spell out parameter and return types
  • Last expression is the return value — semicolon turns it into a statement
  • Use `return` for early exits
  • /// doc comments become HTML via `cargo doc`, doctests run via `cargo test --doc`

Try It Yourself

  1. Write Celsius ↔ Fahrenheit converter functions and call them from main
  2. Write a `stats` function returning `(i32, i32, f64)` for min / max / mean
  3. Add a /// example block and run `cargo test --doc` to verify the doctest passes
Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy