Rust - Closures
Overview
Estimated time: 60–75 minutes
Master Rust's powerful closure system for functional programming. Learn capture modes, the Fn trait family, and how to use closures effectively in real-world applications.
Learning Objectives
- Understand closure syntax and environment capture
- Learn the three closure traits: Fn, FnMut, and FnOnce
- Use closures with iterators and higher-order functions
- Implement functional programming patterns with closures
Prerequisites
What are Closures?
Closures are anonymous functions that can capture variables from their enclosing environment. They're more flexible than regular functions and enable powerful functional programming patterns.
Basic Closure Syntax
fn main() {
// Basic closure syntax
let add = |a, b| a + b;
println!("Add: {}", add(5, 3));
// Closure with explicit types
let multiply: fn(i32, i32) -> i32 = |a, b| a * b;
println!("Multiply: {}", multiply(4, 7));
// Closure with block body
let complex_operation = |x: i32| {
let doubled = x * 2;
let squared = doubled * doubled;
squared + 1
};
println!("Complex: {}", complex_operation(3));
// Closure that captures environment
let factor = 10;
let scale = |x| x * factor; // Captures 'factor'
println!("Scaled: {}", scale(5));
}
Expected output:
Add: 8
Multiply: 28
Complex: 37
Scaled: 50
Checks for Understanding
Question 1: Capture Modes
Q: What's the difference between a closure with and without the move
keyword?
Click to see answer
A: Without move
, closures capture variables by reference (borrowing). With move
, closures take ownership of captured variables, moving them into the closure's environment.
Question 2: Fn Traits
Q: If a closure mutates a captured variable, which Fn trait does it implement?
Click to see answer
A: FnMut
- because it needs mutable access to its environment. It can be called multiple times but requires a mutable reference to self.
Question 3: Returning Closures
Q: Why do we need Box<dyn Fn()>
when returning closures from functions?
Click to see answer
A: Each closure has its own unique type that's generated by the compiler. To return different closures from the same function, we need dynamic dispatch with trait objects, and Box provides heap allocation for the varying-sized closure types.