Functions in Rust
Functions are the building blocks of Rust programs. Rust uses fn to define functions, uses snake_case naming by convention, and has a powerful return-value system that treats functions as expressions.
// Basic function
fn greet(name: &str) {
println!("Hello, {}!", name);
}
// Function with return value
fn add(a: i32, b: i32) -> i32 {
a + b // No semicolon = implicit return
}
// Explicit return
fn max(a: i32, b: i32) -> i32 {
if a > b {
return a; // Early return
}
b // Implicit return
}
fn main() {
greet("Rustacean");
println!("2 + 3 = {}", add(2, 3));
println!("max(5,3) = {}", max(5, 3));
}The key distinction: an expression without a semicolon returns its value. Adding a semicolon turns it into a statement that returns () (the unit type).
fn five() -> i32 {
5 // Expression - returns 5
}
fn nothing() {
5; // Statement - returns () due to semicolon
}
// This would be a compile error:
// fn five_wrong() -> i32 {
// 5; // ERROR: expected i32, found ()
// }Functions as Expressions
In Rust, almost everything is an expression - including if, loop, and blocks. This enables clean, expressive code:
fn main() {
// if as expression
let x = 5;
let description = if x > 0 { "positive" } else { "non-positive" };
println!("{x} is {description}");
// Block as expression
let y = {
let a = 3;
let b = 4;
a * a + b * b // Returns 25 (no semicolon!)
};
println!("y = {y}"); // 25
// loop as expression with break value
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // Return value from loop
}
};
println!("result = {result}"); // 20
}Comments: Three Types
/// Documentation comment - for public APIs
/// Supports Markdown formatting
///
/// # Examples
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
fn add(a: i32, b: i32) -> i32 {
a + b
}
//! Module-level documentation comment
//! Describes the entire module/crate
fn main() {
// Regular comment - ignored by compiler
/* Block comment
spans multiple lines */
let x = 5; // inline comment
// Nested block comments work in Rust!
/* outer /* inner */ still outer */
}Documentation comments (///) are special - they generate HTML documentation via cargo doc. Writing good doc comments is a key part of professional Rust development.
Multiple Return Values via Tuples
/// Returns (min, max) of a slice
fn min_max(numbers: &[i32]) -> (i32, i32) {
let mut min = numbers[0];
let mut max = numbers[0];
for &n in numbers.iter() {
if n < min { min = n; }
if n > max { max = n; }
}
(min, max) // Return tuple
}
/// Returns (quotient, remainder)
fn div_rem(dividend: i32, divisor: i32) -> (i32, i32) {
(dividend / divisor, dividend % divisor)
}
fn main() {
let nums = [3, 7, 1, 9, 2, 8, 4, 6, 5];
let (min, max) = min_max(&nums);
println!("Min: {min}, Max: {max}");
let (q, r) = div_rem(17, 5);
println!("17 / 5 = {q} remainder {r}");
}Recursion
fn factorial(n: u64) -> u64 {
if n <= 1 {
1
} else {
n * factorial(n - 1)
}
}
fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn main() {
for i in 0..=10 {
println!("{}! = {}", i, factorial(i));
}
// Note: naive recursive fibonacci is slow for large n
// We will cover better approaches later
for i in 0..=10 {
print!("{} ", fibonacci(i));
}
println!();
}Rust does not have tail call optimization by default. Deep recursion can overflow the stack. For production code, prefer iterative solutions or use trampolining. The compiler will not warn you about non-tail recursive functions.
🎯 Practice Exercise
- Write a function
is_prime(n: u64) -> boolthat returns true if n is prime - Write a function
nth_prime(n: u32) -> u64that returns the nth prime number - Add documentation comments to both functions with examples
- Write a function that takes a &str and returns (word_count, char_count) as a tuple
- Run
cargo doc --opento see your documentation rendered in a browser
🎉 Key Takeaways
- Functions use
fn name(param: Type) -> ReturnTypesyntax - The last expression without a semicolon is the return value
- Almost everything in Rust is an expression - if, blocks, loops can all return values
- Use
///doc comments for public APIs - they render withcargo doc - Return multiple values using tuples
- Recursion works but watch for stack overflow on deep calls