# 2.2 Function return values > Rust return types, tail expressions as return values, the return keyword, and the missing-semicolon error every beginner meets. Source: https://learnrust.net/chapter-2/return-values/ The functions in the last lesson could *act*, but everything they computed died with them. To participate in their caller's work, functions need to hand values back. In Rust, this is where lesson [1.11](@/chapter-1/expressions-and-statements.md) stops being theory. ## Return types and return values A function that produces a value declares the value's type after an arrow: ```rust fn five() -> i32 { 5 } fn main() { let x = five(); println!("{}", x + 2); } ``` ``` 7 ``` `-> i32` says "calling this function yields an `i32`," and the call expression `five()` evaluates to that value wherever it appears: initializing a variable, sitting inside arithmetic, anywhere an `i32` could go. Now the part Rust does differently, and better. Where's the `return` keyword? Not needed: a function body is a **block**, and you already know blocks evaluate to their tail expression. `5` has no semicolon, so it's the tail, so it's the function's value. The 1.11 rule and the function rule are the *same rule*. A more useful specimen: ```rust fn get_number() -> i32 { let mut input = String::new(); std::io::stdin() .read_line(&mut input) .expect("failed to read input"); input.trim().parse().expect("that wasn't a whole number") } ``` Statements first, then the tail expression delivers the goods. (Two small notes: `std::io::stdin()` is the long-form spelling that skips the `use` line, handy in snippets; and `parse` knows to produce an `i32` here because the function's return type says so. Inference reaching *that* far is very Rust.) ## The error you're guaranteed to meet Because the tail expression does the returning, one reflexive semicolon changes everything: ```rust fn double(x: i32) -> i32 { x * 2; } ``` ``` error[E0308]: mismatched types --> src/main.rs:1:22 | 1 | fn double(x: i32) -> i32 { | ------ ^^^ expected `i32`, found `()` | | | implicitly returns `()` as its body has no tail or `return` expression 2 | x * 2; | - help: remove this semicolon to return this value ``` Decode it with your 1.11 knowledge: the semicolon discarded `x * 2`, the body became all-statements, all-statement blocks evaluate to `()`, and the function promised an `i32`. The compiler knows this mistake so well that the help line is surgical: *remove this semicolon*. You will make this typo within days; now it costs you four seconds. ## The return keyword Rust does have `return`, for exiting *before* the end: ```rust fn classify(n: i32) -> i32 { if n < 0 { return -1; } n * 10 } ``` (A sneak preview of `if`, formally lesson 4.7.) For a negative `n`, the function exits immediately with -1; otherwise it falls through to the tail expression. {% callout(kind="best", title="Best practice") %} Use the tail expression for a function's normal result, and `return` only for early exits. `return n * 10;` as a final line works, but it reads as an accent; idiomatic Rust ends on the bare expression, and rustfmt-formatted code from the ecosystem will train your eye to expect it. {% end %} ## Functions that return nothing No arrow means the function returns `()`, the unit type, like `print_warning` last lesson and like `main` itself. Such functions exist for their side effects. You *can* write `-> ()` explicitly; nobody does. One consequence worth a sentence: since `main` returns `()`, your program signals "success" to the operating system automatically when `main` ends. Programs that need to signal failure get their tools in lessons 7.8 and 12.3. And one warning genre to recognize, when `return` and trailing code mix: ```rust fn answer() -> i32 { return 42; println!("this line is beyond the end of the world"); } ``` ``` warning: unreachable statement --> src/main.rs:3:5 | 2 | return 42; | --------- any code following this expression is unreachable 3 | println!("this line is beyond the end of the world"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement | = note: `#[warn(unreachable_code)]` (part of `#[warn(unused)]`) on by default ``` It compiles, the doomed line never runs, and the compiler tells you so. Code after a `return` is a fossil; delete it or move it above. ## The payoff: don't repeat yourself Remember the chapter 1 finale, with its input-reading recipe pasted twice? Functions retire that duplication: ```rust fn main() { println!("Enter a whole number:"); let x = get_number(); println!("Enter another whole number:"); let y = get_number(); println!("{} + {} is {}", x, y, x + y); } fn get_number() -> i32 { let mut input = String::new(); std::io::stdin() .read_line(&mut input) .expect("failed to read input"); input.trim().parse().expect("that wasn't a whole number") } ``` ``` Enter a whole number: 6 Enter another whole number: 4 6 + 4 is 10 ``` The recipe now exists in exactly one place. If it needs improving (and chapter 12 will improve it), there's one place to improve. Programmers call this the **DRY** principle: Don't Repeat Yourself. Its violation has a name too (WET, "write everything twice"), which tells you how the industry feels about it. ## Quiz time **Question #1** What does this program print? ```rust fn seven() -> i32 { 7 } fn main() { println!("{}", seven() + seven()); } ```
Show solution ``` 14 ``` Each call evaluates to 7, and the call expressions participate in arithmetic like any other values.
**Question #2** Predict the compiler's reaction (error or warning, and roughly what it says): ```rust fn area() -> i32 { 6 * 7; } fn main() { println!("{}", area()); } ```
Show solution Error E0308, mismatched types: the function promises `i32` but the semicolon discards `6 * 7`, so the body evaluates to `()`. The help line says to remove the semicolon. (If you said "expected i32, found ()", full marks.)
**Question #3** A function ends with these two lines. What does the compiler say, and does the program run? ```rust return total; println!("done!"); ```
Show solution It compiles and runs; the `println!` never executes, and the compiler issues an "unreachable statement" warning pointing at it. The `return` above it ends the function every time.
Returning values is half of a function's plumbing. The other half flows the opposite way: getting values *in*. Parameters, next.