RustArena

Step 4 of 6

Statements, expressions and blocks

Why does one semicolon change what a function returns? Rust code is made of two kinds of pieces:

A function body is a list of statements, optionally followed by one final expression without a semicolon. If that final expression is there, its value is the function's result.

Blocks are expressions

Any block { ... } works the same way, not only a function body: its value is its final expression. So you can compute a value in several steps inside a block:

fn main() {
    let price = 1200;
    let total = {
        let shipping = 500;
        let discount = price / 10;
        price + shipping - discount
    };
    println!("total: {total}");
}
total: 1580

shipping and discount exist only inside the block, so they can't be misused later in main. Note the ; after the closing }: the whole let total = { ... }; is one statement.

The unit type ()

What does a function without -> return, then? A special value written () and called unit, meaning "nothing interesting". Its type is also written (). fn greet(name: &str) is short for fn greet(name: &str) -> (). That's what the error in the last step meant by "found ()": the body's last line was a statement, which gives ().

fn log(message: &str) {
    println!("[log] {message}");
}

fn main() {
    let result = log("saving");
    println!("{result:?}");
}
[log] saving
()

You'd never store () in real code (clippy flags it as let_unit_value); this just shows that it's a real value. It has a Debug format ({:?}) but no {} format.

let is a statement

Because let produces no value, you can't use it where a value is expected (unlike C, where x = y = 6 works):

fn main() {
    let x = (let y = 6);
}
error: expected expression, found `let` statement

Common mistakes

A semicolon after a block's final expression makes the block's value ():

fn main() {
    let total = {
        let shipping = 500;
        1200 + shipping;
    };
    println!("total: {total}");
}
error[E0277]: `()` doesn't implement `std::fmt::Display`
 --> src/main.rs:6:22
  |
6 |     println!("total: {total}");
  |                      ^^^^^^^ `()` cannot be formatted with the default formatter

Your task

final_price should return the cost of quantity items plus a shipping charge of 499, but it doesn't compile. Read the error and fix it without using return.

Challenge 2

Compute the number of seconds in a week with a block expression: let seconds_per_week = { ... };, where the block declares seconds_per_day (60 * 60 * 24) and ends with an expression that multiplies it by 7. Then print 604800 seconds in a week.

Challenge 3

Write fn order_total(price_cents: u32, quantity: u32, discount_percent: u32) -> u32. Compute the subtotal (price times quantity), then the discount as subtotal * discount_percent / 100 (integer division, so it rounds down), and return the subtotal minus the discount. Use let statements for the steps and a final expression for the result.

Previous: Returning a value Next: Returning early