RustArena

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

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:

  1. Children under 6 are free (0).
  2. Visitors aged 65 or older pay 600.
  3. Students pay 800.
  4. Everyone else pays 1200.

Previous: Statements, expressions and blocks Next: Building functions from functions