C.W.K.
Stream
Lesson 05 of 06 · published

Functions & Control Flow

~12 min · foundations, functions, control-flow, expressions

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Rust functions are straightforward, with one idea that trips up newcomers: Rust is an expression-based language. Almost everything produces a value, and you'll lean on that constantly.

Defining functions

fn add(a: i32, b: i32) -> i32 { a + b }. Parameter types are always required — Rust never guesses across a function boundary. The return type follows ->. Notice there's no return keyword and no semicolon on a + b: the last expression in a block is the block's value.

Statements vs expressions

A statement does something (let x = 5;); an expression evaluates to a value (5 + 5, a function call, an if, even a { } block). Adding a semicolon turns an expression into a statement and throws its value away. That one semicolon is the difference between returning a value and returning () — the empty 'unit' type.

Control flow that returns values

if is an expression, so let n = if cond { 1 } else { 2 }; works. loop can return a value via break value. And the workhorse is for x in collection — Rust's for always iterates over an iterator, never a manual index, which kills off-by-one bugs at the source.

The missing semicolon is usually intentional. If a function should return a value, its last line has no semicolon. Add one by accident and you'll get a type error saying it found () where your return type was expected — a classic first-week confusion.

Why for-in instead of indexing

Manual index loops are where buffer overruns and off-by-one errors breed. Rust's for item in &items hands you each element directly, the iterator knows when to stop, and there's no index to get wrong.

Code

Expressions everywhere·rust
fn add(a: i32, b: i32) -> i32 {
    a + b           // no semicolon: this IS the return value
}

fn main() {
    let big = if add(2, 3) > 4 { "yes" } else { "no" }; // if is an expression

    // loop can return a value via `break value`
    let mut n = 0;
    let doubled = loop {
        n += 1;
        if n == 5 { break n * 2; }
    };

    // for always iterates an iterator — no manual index
    for i in 1..=3 {
        println!("{i}");
    }
    println!("{big} {doubled}");
}

External links

Exercise

Write fn max(a: i32, b: i32) -> i32 that returns the larger value, using only an if expression — no return keyword. Then add a semicolon to the last expression and read the compiler error.
Hint
Adding the semicolon makes the function return (), which doesn't match the declared -> i32. That error is the key to understanding an expression-based language.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.