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:
- An expression computes a value:
5,a + b,add(2, 3),x > 10. - A statement performs an action and produces no value.
let x = 5;is a statement, and so is any expression followed by;: the semicolon throws the value away.
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
- Forgetting the
;afterlet x = { ... };: the statement never ended, so rustc points just after the}with an error likeexpected `;`, found `println`(naming whatever starts the next line). - Reading
()errors: whenever you see "expectedi32, found()", look for a semicolon at the end of a block or function that shouldn't be there.
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.