Fold and Reduce
🎯 Python has several ways to reduce a collection to a single value:
from functools import reduce
sum([1, 2, 3, 4]) # 10
reduce(lambda acc, s: acc + s, ["a", "b"], "") # "ab"
max([3, 1, 4, 1, 5]) # 5
Rust has the same concepts, but with an important distinction between fold (always has an initial value) and reduce (uses the first element). That distinction matters because Rust handles empty collections differently.
.fold(init, f) — accumulate with an initial value
.fold() is Python's reduce with an explicit starting value. It takes an initial accumulator and a closure that combines the accumulator with each element:
let sum = vec![1, 2, 3, 4].iter().fold(0, |acc, x| acc + x);
// 0 + 1 + 2 + 3 + 4 = 10
let joined = vec!["a", "b", "c"].iter()
.fold(String::new(), |acc, s| acc + s);
// "abc"
The initial value is the identity element for your operation: 0 for addition, 1 for multiplication, "" for string concatenation. On an empty collection, .fold() returns the initial value — no special handling needed.
This is why .fold() returns T directly, not Option<T>. It always has a result.
.reduce(f) — use the first element as the initial value
.reduce() is like .fold() but takes the first element as the starting accumulator. Since the collection might be empty — no first element — it returns Option<T>:
let max = vec![1, 5, 3].into_iter()
.reduce(|a, b| if a > b { a } else { b });
// Some(5)
let empty: Vec<i32> = vec![];
let max = empty.into_iter()
.reduce(|a, b| if a > b { a } else { b });
// None — no elements to reduce
In Python, reduce on an empty iterable without an initial value raises TypeError. Rust returns None instead — same problem, different error-handling philosophy.
.scan() — fold that yields intermediate results
.scan() is like .fold() but produces a value at each step — a running accumulation. It's the equivalent of Python's itertools.accumulate:
# Python
from itertools import accumulate
import operator
list(accumulate([2, 3, 4], operator.mul)) # [2, 6, 24]
// Rust
let running_product: Vec<i32> = vec![2, 3, 4].iter()
.scan(1, |acc, &x| {
*acc *= x;
Some(*acc)
})
.collect();
// [2, 6, 24]
The closure receives a mutable reference to the accumulator (&mut acc) and returns Option<T>. Returning Some(value) yields that value; returning None stops the iteration. The *acc dereference is needed because acc is &mut i32 — you're modifying state through a reference, just like with .iter_mut().
Common shortcuts
For the most common aggregations, Rust provides dedicated methods so you don't need .fold():
.sum()— add all elements (requiresSumtrait).product()— multiply all elements (requiresProducttrait).max()/.min()— find the largest or smallest (returnsOption<T>).count()— count the number of elements
These are consuming adapters — they drive the iterator to completion.
Login to see the full task and start coding.
Topics
This is a premium exercise
Log in to unlock the full exercise and start coding.
Login to access this exercise