Functions are building blocks of any Rust program.
They allow you to organize code into reusable blocks, making programs more readable and maintainable.
This tutorial will cover:
1. What Are Functions in Rust?
A function in Rust is a reusable block of code defined using the fn keyword. Every Rust program starts with the main function, which serves as the entry point.
2. Declaring and Calling Functions
A simple function is defined using the fn keyword followed by its name, parentheses, and a block of code.
Example: Basic Function
fn greet() { println!("Hello, Rust!"); } fn main() { greet(); // Call the function }
3. Parameters and Arguments
Functions can take parameters, which are defined inside the parentheses. Parameters allow functions to operate on dynamic data.
Example: Function with Parameters
fn greet(name: &str) { println!("Hello, {}!", name); } fn main() { greet("Alice"); greet("Bob"); }
4. Returning Values from Functions
Functions can return values using the -> syntax. The return value is the last expression in the function body.
Example: Returning a Value
fn add(a: i32, b: i32) -> i32 { a + b // No semicolon means this is the return value } fn main() { let result = add(5, 3); println!("The sum is: {}", result); }
5. Function with Multiple Parameters
You can pass multiple parameters of different types to a function.
Example: Multiple Parameters
fn describe(name: &str, age: u32) { println!("Name: {}, Age: {}", name, age); } fn main() { describe("Alice", 30); describe("Bob", 25); }
6. Nested Functions
Functions can be defined inside other functions. These are called nested functions and are typically used for organization or encapsulation.
Example: Nested Function
fn main() { fn helper() { println!("This is a nested function."); } println!("Calling the nested function:"); helper(); }
7. Closures and Anonymous Functions
Closures are anonymous functions that can capture variables from their enclosing scope.
Example: Basic Closure
fn main() { let add = |a: i32, b: i32| a + b; // Closure println!("Sum: {}", add(5, 3)); }
Example: Closure Capturing Variables
fn main() { let multiplier = 2; let multiply = |x: i32| x * multiplier; // Captures `multiplier` from the scope println!("Result: {}", multiply(5)); }
8. Practical Examples
8.1 Calculating Factorials
fn factorial(n: u32) -> u32 { if n == 0 { 1 } else { n * factorial(n - 1) } } fn main() { let num = 5; println!("Factorial of {} is {}", num, factorial(num)); }
8.2 Checking if a Number Is Prime
fn is_prime(n: u32) -> bool { if n <= 1 { return false; } for i in 2..n { if n % i == 0 { return false; } } true } fn main() { let num = 13; println!("Is {} prime? {}", num, is_prime(num)); }
8.3 Function Returning a Tuple
fn min_max(a: i32, b: i32) -> (i32, i32) { if a < b { (a, b) } else { (b, a) } } fn main() { let (min, max) = min_max(10, 20); println!("Min: {}, Max: {}", min, max); }
8.4 Using Closures for Sorting
fn main() { let mut numbers = vec![5, 2, 8, 1, 3]; numbers.sort_by(|a, b| b.cmp(a)); // Sort in descending order using a closure println!("Sorted numbers: {:?}", numbers); }
8.5 Function as Parameters
Functions in Rust can be passed as parameters to other functions.
fn apply<F>(f: F, x: i32) -> i32 where F: Fn(i32) -> i32, { f(x) } fn square(x: i32) -> i32 { x * x } fn main() { let result = apply(square, 5); println!("Result: {}", result); }
8.6 Using Generic Functions
Generics allow you to create functions that work with any type.
fn display<T: std::fmt::Debug>(item: T) { println!("{:?}", item); } fn main() { display(42); // Integer display("Hello Rust"); // String slice display(vec![1, 2, 3]); // Vector }
9. Summary
Key Features of Functions in Rust
- Functions are defined using the fn keyword.
- Parameters must include type annotations.
- Functions can return values using the -> syntax.
- Closures provide an expressive way to create anonymous functions.
- Rust supports higher-order functions and generics for flexibility.
Practical Use Cases
- Encapsulating logic in reusable blocks.
- Recursion (e.g., calculating factorials or Fibonacci numbers).
- Returning multiple values using tuples.
- Implementing dynamic behavior with closures.
By mastering functions, you can write clean, modular, and efficient Rust code.