Step 3 of 6
Returning a value
Functions can also hand a value back to their caller: a return value. Declare its type after an arrow ->:
fn main() {
let total = add(2, 3);
println!("2 + 3 = {total}");
println!("double 21 = {}", double(21));
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn double(x: i32) -> i32 {
x * 2
}
2 + 3 = 5
double 21 = 42
Look at the body of add: just a + b, with no semicolon. In Rust, when the last line of a function body has no semicolon, its value is what the function returns. The call add(2, 3) is then replaced by that value, so you can store it, print it, or use it in another calculation, like add(add(1, 2), 3).
There is also a return keyword: return a + b; works too. At the very end of a function it isn't idiomatic, though: Rust code lets the last expression be the result, and clippy flags a final return (needless_return). You'll see where return is the right tool two steps from now.
The semicolon trap
Add a ; after the last line and it becomes a statement that produces nothing, so the function returns nothing:
fn add(a: i32, b: i32) -> i32 {
a + b;
}
fn main() {
println!("{}", add(2, 3));
}
error[E0308]: mismatched types
--> src/main.rs:1:27
|
1 | fn add(a: i32, b: i32) -> i32 {
| --- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
2 | a + b;
| - help: remove this semicolon to return this value
The help: line tells you exactly what to do. The next step explains what () is.
Common mistakes
- Forgetting
-> type: then the function is declared to return nothing, and a finala + bis an error (expected(), foundi32). The help line suggeststry adding a return type: -> i32. - Returning the wrong type:
fn half(x: i32) -> i32 { x as f64 / 2.0 }doesn't compile. The declared type is a promise. - Printing instead of returning: a function that only
println!s its result can't be used in further calculations. Functions that compute something should return it, and let the caller decide what to do with it. Tests (at work and on this site) check return values.
How exercises work from here
From now on, many exercises ask you to write a function rather than a whole program. Hidden tests call your function with different arguments and check what it returns, like assert_eq!(area(3, 4), 12). You don't need a main (if you keep one, it's ignored). If a test fails, you'll see the call, the expected value and what your function returned.
Starter code often contains todo!(): a placeholder that compiles but stops the program with "not yet implemented" if it runs. Replace it with your code.
Your task
Write fn area(width: u32, height: u32) -> u32 that returns the area of a rectangle. Return it as the last expression (no return keyword).
Challenge 2
Write fn perimeter(width: u32, height: u32) -> u32 that returns the distance around a rectangle: twice the width plus twice the height.
Challenge 3
Write a function average from scratch. It takes three f64 parameters, a, b and c, and returns their average as an f64.
Previous: Parameters Next: Statements, expressions and blocks