In Rust, almost everything is an expression — a piece of code that produces a value. Even a whole { ... } block is an expression: it evaluates to its last expression, as long as that last expression has no semicolon.
let y = {
let a = 3;
a + 1 // no semicolon: this value flows out of the block
};
// y is now 4
Functions work exactly the same way: a function's body is a block, so a function returns its last expression automatically. You only need the return keyword for early exits.
fn add_one(n: i32) -> i32 {
n + 1 // this is the return value — no `return`, no semicolon
}
The trap: one accidental semicolon
A semicolon turns an expression into a statement, and a statement's value is () — the empty "unit" type. So writing n * n; as the last line makes the function return () instead of a number, and the compiler stops you:
error[E0308]: mismatched types
--> trap.rs:1:22
|
1 | fn square(n: i32) -> i32 {
| ------ ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | n * n;
| - help: remove this semicolon to return this value
error: aborting due to 1 previous error
Read the help line — the compiler even tells you the fix: remove this semicolon to return this value. Every Rust programmer hits this error; now you know exactly what it means.
Assignment
Follow the Coding Tutorial:
- Define a function
square(n: i32) -> i32whose body is justn * n— noreturn, no semicolon. - Bind
doubledto a block that declareslet x = 21;and ends with the expressionx * 2. - Print both results.
The expected output is:
doubled = 42
square(6) = 36
Hint
If you see expected `i32`, found `()`, look for a semicolon on the line that should produce the value.
Free plan: 30 tutor messages. Premium is unlimited.