Fold and Scan
The fold() and scan() methods are powerful iterator adapters that allow you to maintain state across iteration. They're essential for tasks like computing running totals, building complex aggregations, or transforming sequences based on accumulated state.
Understanding fold()
fold() reduces an iterator to a single value by repeatedly applying an accumulator function. It takes an initial value and a closure that combines the accumulator with each element:
let numbers = vec![1, 2, 3, 4, 5];
// Sum all numbers
let sum = numbers.iter().fold(0, |acc, &x| acc + x);
assert_eq!(sum, 15);
// Product of all numbers
let product = numbers.iter().fold(1, |acc, &x| acc * x);
assert_eq!(product, 120);
// Build a string
let words = vec!["Hello", "World"];
let sentence = words
.iter()
.fold(String::new(), |acc, &word| {
if acc.is_empty() {
word.to_string()
} else {
format!("{} {}", acc, word)
}
});
assert_eq!(sentence, "Hello World");Understanding scan()
scan() is similar to fold() but yields intermediate values at each step, producing a new iterator. It maintains mutable state and returns Option<T> values:
let numbers = vec![1, 2, 3, 4, 5];
// Running sum - yields cumulative totals
let running_sums: Vec<i32> = numbers
.iter()
.scan(0, |state, &x| {
*state += x;
Some(*state)
})
.collect();
assert_eq!(running_sums, vec![1, 3, 6, 10, 15]);
// Running product
let running_products: Vec<i32> = numbers
.iter()
.scan(1, |state, &x| {
*state *= x;
Some(*state)
})
.collect();
assert_eq!(running_products, vec![1, 2, 6, 24, 120]);Key Differences
fold(): Consumes the entire iterator and returns a single final valuescan(): Returns an iterator of intermediate results; can stop early by returningNone
// scan can stop early
let numbers = vec![1, 2, 3, 4, 5];
let until_exceeds_5: Vec<i32> = numbers
.iter()
.scan(0, |state, &x| {
*state += x;
if *state <= 5 {
Some(*state)
} else {
None // Stop iteration
}
})
.collect();
// Stops before exceeding 5
assert_eq!(until_exceeds_5, vec![1, 3]);Your Task
Implement the following functions that demonstrate fold() and scan() patterns:
sum_with_fold(numbers: &[i32]) -> i32- Sum all numbers usingfold()product_with_fold(numbers: &[i32]) -> i64- Multiply all numbers usingfold()(use i64 to avoid overflow)concat_strings(strings: &[&str], separator: &str) -> String- Join strings with a separator using
fold()
- Join strings with a separator using
running_sum(numbers: &[i32]) -> Vec<i32>- Return cumulative sums using
scan()
- Return cumulative sums using
running_max(numbers: &[i32]) -> Vec<i32>- Return running maximum values using
scan()
- Return running maximum values using
take_while_sum_under(numbers: &[i32], limit: i32) -> Vec<i32>- Take numbers while running sum stays under limit usingscan()count_occurrences<T: PartialEq>(items: &[T], target: &T) -> usize- Count occurrences using
fold()
- Count occurrences using
running_average(numbers: &[f64]) -> Vec<f64>- Return running average at each position using
scan()
- Return running average at each position using
Examples
// sum_with_fold
assert_eq!(sum_with_fold(&[1, 2, 3, 4, 5]), 15);
// product_with_fold
assert_eq!(product_with_fold(&[1, 2, 3, 4]), 24);
// concat_strings
assert_eq!(concat_strings(&["a", "b", "c"], "-"), "a-b-c");
// running_sum
assert_eq!(running_sum(&[1, 2, 3, 4]), vec![1, 3, 6, 10]);
// running_max
assert_eq!(
running_max(&[3, 1, 4, 1, 5]),
vec![3, 3, 4, 4, 5]
);
// take_while_sum_under
// Takes [1, 2, 3] but stops before adding 4
// (sum would be 10 >= 10)
assert_eq!(
take_while_sum_under(&[1, 2, 3, 4, 5], 10),
vec![1, 2, 3]
);
// count_occurrences
assert_eq!(count_occurrences(&[1, 2, 1, 3, 1], &1), 3);
// running_average
assert_eq!(
running_average(&[2.0, 4.0, 6.0]),
vec![2.0, 3.0, 4.0]
);Hints
Click here for hints
- For
fold(), the closure signature is|accumulator, element| -> new_accumulator - For
scan(), the closure signature is|&mut state, element| -> Option<output> - In
scan(), you modifystatein place using*state = ... - Use
scan()returningNoneto stop iteration early - For
running_average, track both the sum and count in the state (use a tuple) product_with_folduses i64 to handle larger products safely- Empty slices should return sensible defaults (0 for sum, 1 for product, empty string, etc.)
No run yet
Press
Run to execute your code and see test results here.