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.
() 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.