Rust — Functions
Function anatomy
fn add(a: i32, b: i32) -> i32 {
a + b
}
| Piece | Rule |
|---|---|
fn | keyword that starts every function |
add | snake_case by convention (enforced by compiler warnings) |
a: i32 | parameter — type is mandatory, always |
-> i32 | return type after the arrow |
Rust never infers function parameter or return types — signatures are contracts, checked everywhere the function is called.
Return via final expression (no return needed)
The last expression in the body IS the return value:
fn square(x: i32) -> i32 {
x * x // no semicolon!
}
Semicolon = statement = returns () (nothing). This is Rust's most common beginner error:
fn broken(x: i32) -> i32 {
x * x; // ❌ semicolon discards the value
}
Explicit return — for early exits
fn classify(n: i32) -> &'static str {
if n < 0 {
return "negative"; // early return uses `return`
}
if n == 0 {
return "zero";
}
"positive" // happy path stays implicit
}
Convention: use return only for early exits; let the final line be the value.
Unit type — "returns nothing"
Functions without a return type implicitly return (), Rust's empty tuple:
fn greet(name: &str) { // -> () implied
println!("Hello, {}", name);
}
Multiple parameters & defaults don't exist
fn volume(w: f64, h: f64, d: f64) -> f64 {
w * h * d
}
No default parameter values in Rust — the pattern instead:
fn connect(timeout_secs: Option<u64>) { … }
connect(None); // caller decides explicitly
connect(Some(30));
Statements inside are fine
fn analyze(nums: &[i32]) -> (i32, f64) {
let sum: i32 = nums.iter().sum();
let count = nums.len();
let avg = if count == 0 {
0.0
} else {
sum as f64 / count as f64
};
(sum, avg) // returning a tuple!
}
let (total, average) = analyze(&[90, 80, 70]);
Tuples let one function hand back several related values.
Calling functions
fn main() {
let result = add(2, 3); // arguments match declared types exactly
println!("2+3 = {}", result);
// nested calls compose naturally:
println!("{}", add(square(2), square(3))); // 13
}
fn square(x: i32) -> i32 { x * x }
fn add(a: i32, b: i32) -> i32 { a + b }
Order of definition doesn't matter — no forward declarations needed.
Gotchas: missing
self-style confusion comes later with methods ·snake_caseviolations produce warnings · returning a reference needs lifetime talk (later lesson) · integer overflow in pure functions panics in debug.
Mini Practice
- celsius_to_fahrenheit(f64) -> f64; test 100 → 212.
- max_of_three(i32,i32,i32) using two ifs.
- Return
(min, max)tuple from a slice. - Early-return validator for username length 3–16.
- Break square() with a semicolon; internalize the error.
Next: scope →
Related Topics
Frequently Asked Questions about Functions
What is Functions in Rust?
Functions is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.
How do I learn Functions?
Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Functions.
Why is Functions important in Rust?
Functions is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.