Step 5 of 6
Returning early
return value; ends the function immediately, wherever it is, and hands back that value. At the end of a function you don't need it, but it's exactly right when a function should stop early, usually for a special case.
To decide whether to return early, you need a condition. The next module covers if in depth; here's the short version: if condition { ... } runs the code in the braces only when the condition (a bool) is true.
fn ticket_price(age: u32) -> u32 {
if age < 3 {
return 0;
}
if age < 12 {
return 700;
}
1200
}
fn main() {
println!("age 2: {}", ticket_price(2));
println!("age 8: {}", ticket_price(8));
println!("age 30: {}", ticket_price(30));
}
age 2: 0
age 8: 700
age 30: 1200
For a 2-year-old, the first check returns 0 and nothing after it runs. For an 8-year-old, the first condition is false, so the program moves on and the second check returns 700. Everyone else reaches the final expression, 1200.
Checks at the top of a function that handle special cases and return are called guard clauses. They keep the main logic at the bottom flat and easy to read, and they're very common in Rust at work, especially for rejecting invalid input.
Protecting against crashes
Integer division by zero panics, so a guard clause can handle that case first:
fn per_person(total: u32, people: u32) -> u32 {
if people == 0 {
return 0;
}
total / people
}
fn main() {
println!("{}", per_person(90, 4));
println!("{}", per_person(90, 0));
}
22
0
Returning 0 quietly hides the problem from the caller. Later you'll meet Option and Result, which let a function say "there is no answer" properly.
Common mistakes
Every path through the function must produce a value. If only the special case returns, the compiler notices the missing value for every other case:
fn sign(n: i32) -> i32 {
if n < 0 {
return -1;
}
}
fn main() {
println!("{}", sign(5));
}
error[E0317]: `if` may be missing an `else` clause
--> src/main.rs:2:5
|
1 | fn sign(n: i32) -> i32 {
| --- expected `i32` because of this return type
2 | / if n < 0 {
3 | | return -1;
4 | | }
| |_____^ expected `i32`, found `()`
The fix here is a final expression after the if (for example 1).
- A final
returnout of habit:return x * 2;as the last line works, but idiomatic Rust ends withx * 2, and clippy says so (needless_return). - Unreachable code: anything right after a
returnin the same block never runs, and the compiler warnsunreachable statement.
Your task
Write fn safe_divide(a: i32, b: i32) -> i32 that returns a / b, except that it returns 0 when b is 0 (instead of crashing). Use an early return.
Challenge 2
Write fn average_score(total_points: u32, games: u32) -> u32 that returns the average points per game (integer division), or 0 when no games have been played.
Challenge 3
Write fn ticket_price(age: u32, is_student: bool) -> u32 for a museum, in cents. Apply the rules in this order, each with a guard clause:
- Children under 6 are free (
0). - Visitors aged 65 or older pay
600. - Students pay
800. - Everyone else pays
1200.
Previous: Statements, expressions and blocks Next: Building functions from functions