Functional programming emphasizes pure functions, immutability, and declarative code. Rust, while not purely functional, incorporates many functional programming features, notably Iterators and Closures, which enable concise, expressive, and often more efficient code for data processing.
Iterators
An iterator in Rust is a sequence of values. It's not the data itself but a *way to produce* a sequence of values from data. The core of Rust's iteration is the `Iterator` trait, which defines the `next` method. When `next` is called, it returns an `Option<Self::Item>`, where `Self::Item` is the type of the value produced by the iterator. It returns `Some(value)` if there's a value, and `None` when the iteration is complete.
Rust's iterators are *lazy*. This means they don't perform any computation until an adapter method (like `map`, `filter`) or a consumer method (like `collect`, `sum`, `for_each`) is called that needs to produce values. This allows for efficient chaining of operations without creating intermediate collections.
There are several ways to get an iterator from a collection:
* `iter()`: Borrows each element by immutable reference (`&T`).
* `iter_mut()`: Borrows each element by mutable reference (`&mut T`).
* `into_iter()`: Consumes the collection, moving ownership of each element out.
Iterators come with a rich set of *adapter* methods (e.g., `map`, `filter`, `flat_map`, `take`, `skip`) that transform one iterator into another, and *consumer* methods (e.g., `sum`, `collect`, `for_each`, `fold`, `any`, `all`) that consume the iterator and produce a final result.
Closures
Closures in Rust are anonymous functions that can capture values from the scope in which they are defined. Unlike regular functions, closures can 'close over' their environment, meaning they can access variables from the enclosing scope. This makes them incredibly powerful for tasks like defining custom behavior for iterator methods (e.g., the mapping logic in `map` or the filtering logic in `filter`).
Closures are implemented via three traits: `Fn`, `FnMut`, and `FnOnce`, which dictate how they capture and interact with their environment:
* `Fn`: Can capture variables by immutable reference (`&T`). It can be called multiple times without modifying the captured environment.
* `FnMut`: Can capture variables by mutable reference (`&mut T`). It can be called multiple times and can modify the captured environment.
* `FnOnce`: Can capture variables by value (`T`). It consumes the captured variables and can therefore be called only once.
The syntax for a closure is `|parameters| { body }`. If the body is a single expression, the braces can be omitted.
Iterators and Closures Together
Iterators and closures are often used in conjunction to write functional-style code in Rust. Closures provide the dynamic logic needed by iterator adapter methods (like the transformation logic for `map` or the predicate for `filter`), allowing for highly flexible and readable data processing pipelines. This combination promotes a declarative style, where you describe *what* you want to achieve rather than *how* to achieve it with explicit loops and mutable state.
Example Code
use std::collections::HashMap;
fn main() {
// --- Iterators Basic Example ---
let numbers = vec![1, 2, 3, 4, 5];
println!("\n--- Basic Iterator Use ---");
// Using .iter() to get an immutable iterator
let mut num_iter = numbers.iter();
// Calling .next() manually
println!("First number: {:?}", num_iter.next());
println!("Second number: {:?}", num_iter.next());
// A for loop implicitly uses into_iter() for Vec<T>
println!("Numbers using for loop:");
for num in &numbers { // &numbers creates an iterator of immutable references
print!("{} ", num);
}
println!();
// --- Closures Basic Example ---
println!("\n--- Basic Closure Use ---");
// A simple closure that adds two numbers
let add_one = |x| x + 1;
println!("5 + 1 = {}", add_one(5));
// A closure that captures its environment (by immutable reference by default)
let factor = 10;
let multiply_by_factor = |x| x * factor; // 'factor' is captured immutably
println!("3 * factor ({}) = {}", factor, multiply_by_factor(3));
// A closure that captures its environment mutably (FnMut)
let mut counter = 0;
let mut increment_counter = |amount| {
counter += amount; // 'counter' is captured mutably
println!("Counter incremented by {}. New value: {}", amount, counter);
};
increment_counter(5);
increment_counter(3);
println!("Final counter value: {}", counter);
// A closure that consumes its environment (FnOnce)
let my_string = String::from("hello");
let consume_string = || {
// 'my_string' is moved into the closure, so it can only be called once
println!("Consumed string: {}", my_string);
};
consume_string();
// println!("Original string after consume: {}", my_string); // This would be a compile error (use of moved value)
// --- Iterators and Closures Working Together ---
println!("\n--- Iterators and Closures Together ---");
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Example 1: Filter even numbers and then map them to their squares
let even_squares: Vec<i32> = numbers
.iter()
.filter(|&&num| num % 2 == 0) // Closure as predicate for filter
.map(|&num| num * num) // Closure as transformation for map
.collect(); // Consumer method
println!("Even numbers squared: {:?}", even_squares);
// Example 2: Sum all odd numbers greater than 3
let sum_odd_gt_3: i32 = numbers
.into_iter() // Consumes numbers, so we can't use 'numbers' again
.filter(|&num| num % 2 != 0 && num > 3)
.sum(); // Consumer method
println!("Sum of odd numbers greater than 3: {}", sum_odd_gt_3);
// Example 3: Using fold to calculate product of numbers
let data = vec![1, 2, 3, 4];
let product = data
.iter()
.fold(1, |acc, &num| acc * num); // Closure defines how to accumulate
println!("Product of [1, 2, 3, 4]: {}", product);
// Example 4: Creating a HashMap from a vector of tuples using collect and closures
let pairs = vec![("apple", 1), ("banana", 2), ("orange", 3)];
let fruit_map: HashMap<&str, i32> = pairs
.into_iter()
.collect(); // Works because the iterator yields (K, V) which HashMap can consume
println!("Fruit map: {:?}", fruit_map);
// Example 5: Custom filter with mutable closure
let mut threshold = 5;
let items = vec![1, 10, 3, 8, 2, 7];
// Closure captures 'threshold' by mutable reference to modify it.
// Note: Calling this filter multiple times on the same iterator might have unexpected side effects
// due to 'threshold' changing. For simple filtering, a non-mutating closure is preferred.
let filtered_items: Vec<i32> = items
.iter()
.filter_map(|&item| {
if item > threshold {
threshold += 1; // Mutates captured 'threshold'
Some(item) // Include item if it's greater than current threshold
} else {
None
}
})
.collect();
println!("Items filtered with dynamic threshold (initial {}): {:?}", 5, filtered_items);
println!("Final threshold value: {}", threshold);
}








Functional Language Features: Iterators and Closures