05. Functions, Expressions, Return Values, Doc Comments
Defining functions with `fn name(arg: T) -> R { ... }`, the distinction between expressions and statements, returning implicitly via the last expression vs. explicit `return`, and `///` doc comments that turn into HTML via `cargo doc`. This lesson sets you up to write idiomatic functional Rust.
What you'll learn
- 1Define functions with explicit parameter and return types
- 2Tell expressions from statements
- 3Return implicitly from the last expression β no explicit `return`
- 4Write `///` doc comments and build HTML with `cargo doc`
- 5Return multiple values via tuples
Overview
Rust functions are straightforward, but expression-orientation trips up people from imperative languages β **whether you put a semicolon on the last line decides whether anything is returned**. Once that clicks, code gets dramatically shorter.
Core Concepts
1) Function syntax
fn add(a: i32, b: i32) -> i32 {
a + b // no semicolon β this is the return value
}2) Expression vs. statement
- **Expression** β produces a value. `5`, `a + b`, `if cond { 1 } else { 2 }`, blocks `{ let x = 1; x + 1 }`
- **Statement** β performs an action but produces no value. `let x = 5;`, `fn f() {}`
- Adding a semicolon to an expression turns it into a statement β value is discarded
3) Explicit return
Use `return value;` when you want an early exit. For the normal happy path the trailing-expression form is more idiomatic.
4) Doc comments
- `///` declares documentation for the next item
- Markdown is supported β code blocks become automatic doctests
- `cargo doc --open` generates and opens HTML docs
Hands-on Examples
Implicit return alongside explicit `return`:
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
return -1; // early exit
}
a / b // last expression β returned
}
fn main() {
println!("{}", divide(10, 3)); // 3
println!("{}", divide(10, 0)); // -1
}Return multiple values via a tuple:
fn minmax(v: &[i32]) -> (i32, i32) {
let mut mn = v[0];
let mut mx = v[0];
for &x in v {
if x < mn { mn = x; }
if x > mx { mx = x; }
}
(mn, mx)
}
fn main() {
let (a, b) = minmax(&[3, 7, 1, 9, 4]);
println!("min={}, max={}", a, b); // 1, 9
}Doc comments β Markdown + doctest:
/// Add two numbers and return the sum.
///
/// # Examples
///
/// ```
/// assert_eq!(my_crate::add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}cargo doc --open # generate and open HTML docs
cargo test --doc # run doctestsCommon Mistakes
Q. My return value disappeared β I wrote `a + b` and got nothing back
A. `a + b;` (with semicolon) is a statement, the value is dropped. The trailing expression must have **no semicolon**.
Q. What's the unit type `()`?
A. Roughly equivalent to `void` elsewhere. Omitting the return type means `-> ()` β the function returns no meaningful value.
Q. Can main return a value?
A. Yes β `fn main() -> Result<(), Box<dyn Error>>` enables `?` inside main. Covered in the error-handling lessons.
Recap
- Function signatures spell out parameter and return types
- Last expression is the return value β semicolon turns it into a statement
- Use `return` for early exits
- /// doc comments become HTML via `cargo doc`, doctests run via `cargo test --doc`
Try It Yourself
- Write Celsius β Fahrenheit converter functions and call them from main
- Write a `stats` function returning `(i32, i32, f64)` for min / max / mean
- Add a /// example block and run `cargo test --doc` to verify the doctest passes
All lecture materials and example code are openly available on GitHub.
View on GitHub β