Functions in Rust: parameters, return values and expressions
How to write Rust functions with typed parameters and return values, why the last line has no semicolon, and expressions vs statements.
A function is declared with fn, its parameters (each with a type), and a return type after ->:
fn area(width: u32, height: u32) -> u32 {
width * height
}
fn main() {
let a = area(4, 5);
println!("area {a}");
}
area 20
Parameter types are always required; Rust infers types inside a function body, but never across a function's signature. That keeps every function readable on its own and makes error messages point at the right place.
The last expression is the return value
There's no return in area. In Rust, a block's value is its last expression, the part written without a semicolon. A function body is a block, so width * height is what area returns.
The difference between expressions (which produce a value) and statements (which don't) is central to Rust:
width * height,area(2, 3),if x > 0 { 1 } else { -1 }and a{ ... }block are expressions.let x = 5;is a statement. Adding;after an expression turns it into a statement and throws the value away.
Use return value; for early exits, typically inside an if near the top of the function (a guard clause). For the normal result at the end, idiomatic Rust leaves out return; clippy flags return x; as the last line (needless_return).
Functions with no -> return the unit type (), an empty value meaning "nothing useful". fn log(msg: &str) is short for fn log(msg: &str) -> ().
Blocks and if are expressions too
fn main() {
let score = 82;
let grade = if score >= 90 { 'A' } else if score >= 75 { 'B' } else { 'C' };
let bonus = {
let base = score / 10;
base * 2
};
println!("{grade} {bonus}");
}
B 16
Every branch of an if used as a value must have the same type, and there must be an else.
Common mistakes
A semicolon after the final expression is the number one beginner error:
fn area(width: u32, height: u32) -> u32 {
width * height;
}
fn main() {
println!("{}", area(2, 3));
}
error[E0308]: mismatched types
--> src/main.rs:1:37
|
1 | fn area(width: u32, height: u32) -> u32 {
| ---- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | width * height;
| - help: remove this semicolon to return this value
With the ;, the body ends in a statement, so the function would return (), not a u32. Other common ones:
- Missing a parameter type:
fn greet(name)fails with "expected one of:,@, or|". - Wrong number of arguments: E0061, "this function takes 2 arguments but 1 argument was supplied". Rust has no default or optional parameters; use an
Optionparameter or a second function. - Expecting a function to change its argument. Numbers are copied into parameters. To change the caller's value, take
&mut(see borrowing) or return the new value.
Example
fn area(width: u32, height: u32) -> u32 {
width * height
}
fn grade(score: u32) -> char {
if score > 100 {
return '?';
}
if score >= 90 {
'A'
} else if score >= 75 {
'B'
} else {
'C'
}
}
fn log_line(msg: &str) {
println!("[log] {msg}");
}
fn min_max(a: i32, b: i32) -> (i32, i32) {
if a < b { (a, b) } else { (b, a) }
}
fn main() {
log_line("computing");
let room = area(4, 5);
let half = {
let h = room / 2;
h + 1
};
println!("area {room}, half plus one {half}");
println!("grades: {} {} {} {}", grade(95), grade(80), grade(60), grade(120));
let (lo, hi) = min_max(9, 3);
println!("min {lo}, max {hi}");
}
Output:
[log] computing
area 20, half plus one 11
grades: A B C ?
min 3, max 9