4.0 What This Teaches
- Declaring functions with
fn - Parameters and required type annotations
- Return values and the
->syntax - Expressions vs. statements - the key to Rust's implicit return
- Early return with
return - Returning multiple values via tuples
4.1 Declaring a Function
fn keyword declares a function. The body is enclosed in { }.fn greet() {
println!("Hello from greet()");
}
greet();
(). You rarely write () explicitly - omitting the return type
annotation means unit is assumed.
4.2 Parameters
fn add(a: i32, b: i32) -> i32 {
a + b
}
-> introduces the return type. Here both parameters are
i32 and the function returns i32.
name: Type pair - you cannot write a, b: i32 to share a
type across two names.
4.3 Expressions vs. Statements
- A statement performs an action and produces no value. It ends with
;. - An expression evaluates to a value. It has no
;.
return
keyword needed:
fn add(a: i32, b: i32) -> i32 {
a + b // expression - becomes the return value
}
a + b into a statement, which produces
(). If the declared return type is i32, the compiler
rejects it:
fn add(a: i32, b: i32) -> i32 {
a + b; // now a statement - returns (), not i32 - compile error
}
if, loop, and match
blocks too, not just function bodies.
4.4 Early Return
return to exit before reaching the end of the function. This is
common when you find a result inside a loop or want to handle an error condition
immediately:
fn first_positive(values: &[i32]) -> i32 {
for &v in values {
if v > 0 {
return v;
}
}
-1 // tail expression: reached only if the loop found nothing
}
&[i32] parameter type is a slice - a reference to a
sequence of i32 values. Slices let you pass arrays and vectors to the
same function without copying them.
return for early exits. At the natural end of a function, prefer
the tail expression style - it is more idiomatic and easier to read.
4.5 Returning Multiple Values
fn min_max(a: i32, b: i32) -> (i32, i32) {
if a < b { (a, b) } else { (b, a) }
}
let (lo, hi) = min_max(9, 3);
println!("min = {lo}, max = {hi}");
if here is an expression - both arms produce (i32, i32)
tuples, and the whole if expression is the function's return value.
4.6 Example - All Together
// Functions - demonstrates function definitions, parameters, return values, and expressions in Rust.
fn greet() {
println!("Hello from greet()");
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn first_positive(values: &[i32]) -> i32 {
for &v in values {
if v > 0 {
return v;
}
}
-1
}
fn min_max(a: i32, b: i32) -> (i32, i32) {
if a < b { (a, b) } else { (b, a) }
}
fn main() {
greet();
let sum = add(3, 4);
println!("add(3, 4) = {sum}");
let nums = [-2, -1, 5, 8];
println!("first_positive = {}", first_positive(&nums));
let (lo, hi) = min_max(9, 3);
println!("min = {lo}, max = {hi}");
}
Hello from greet()
add(3, 4) = 7
first_positive = 5
min = 3, max = 9
4.7 Exercise
Exercise
- Write a function
square(n: i32) -> i32that returnsnmultiplied by itself. Use a tail expression, notreturn. - Write a function
clamp(value: i32, lo: i32, hi: i32) -> i32that returnsvalueclamped to the range[lo, hi]. Use earlyreturnfor at least one of the boundary checks. - Write a function
swap(a: i32, b: i32) -> (i32, i32)that returns the two values in reversed order. Call it inmainand destructure the result.
4.8 Common Mistakes
Semicolon on the tail expression
fn double(x: i32) -> i32 {
x * 2; // error: expected i32, found ()
}
x * 2 must remain an expression.Missing parameter type
fn add(a, b: i32) -> i32 { ... } // error: expected `:`
a: i32, b: i32.Forgetting -> for the return type
fn add(a: i32, b: i32) i32 { ... } // syntax error
-> is required between the parameter list and the return type.Treating return as required
return at the end of every function works, but is not idiomatic
Rust. The tail expression form is preferred because it reads as "this function
evaluates to X" rather than "exit this function carrying X."
4.9 Key Terms
| Term | Meaning |
|---|---|
| fn | Keyword that declares a function |
| parameter | A named input to a function; always requires a type annotation |
| -> | Separates the parameter list from the return type |
| expression | Code that evaluates to a value; no trailing ; |
| statement | Code that performs an action; ends with ;; produces () |
| tail expression | The final expression in a block; becomes the block's value |
| return | Exits the function early, carrying the given value |
| unit type () | The type returned by functions that produce no meaningful value |
| tuple | A fixed-size ordered collection of values, possibly of different types |
| slice &[T] | A reference to a contiguous sequence of T values |