Step 5 of 7
Consumers: sum, count, min, max, fold, any, find
The consumer is the last link of a chain: it pulls the items through and produces the answer. Here are the ones you'll use daily, on a list of orders:
struct Order {
id: u32,
customer: String,
total: f64,
shipped: bool,
}
fn main() {
let orders = vec![
Order { id: 1, customer: String::from("ann"), total: 25.0, shipped: true },
Order { id: 2, customer: String::from("bo"), total: 140.5, shipped: false },
Order { id: 3, customer: String::from("ann"), total: 9.99, shipped: true },
Order { id: 4, customer: String::from("cy"), total: 60.0, shipped: false },
];
let revenue: f64 = orders.iter().map(|o| o.total).sum();
let pending = orders.iter().filter(|o| !o.shipped).count();
let biggest = orders.iter().max_by(|a, b| a.total.total_cmp(&b.total));
let for_bo = orders.iter().find(|o| o.customer == "bo");
let first_big = orders.iter().position(|o| o.total > 100.0);
let all_positive = orders.iter().all(|o| o.total > 0.0);
let any_huge = orders.iter().any(|o| o.total > 1000.0);
println!("revenue: {revenue:.2}");
println!("pending: {pending}");
println!("biggest: {:?}", biggest.map(|o| o.id));
println!("bo's order: {:?}", for_bo.map(|o| o.id));
println!("first over 100 at index {:?}", first_big);
println!("all positive: {all_positive}, any over 1000: {any_huge}");
}
revenue: 235.49
pending: 2
biggest: Some(2)
bo's order: Some(2)
first over 100 at index Some(1)
all positive: true, any over 1000: false
More consumers, and fold, the most general one:
fn main() {
let words = ["kiwi", "banana", "fig", "cherry"];
let shortest = words.iter().min_by_key(|w| w.len());
let longest = words.iter().max_by_key(|w| w.len());
println!("{:?} {:?}", shortest, longest);
let factorial: u64 = (1..=10).product();
println!("10! = {factorial}");
// fold(start, |accumulator, item| next accumulator)
let balance = [50, -20, 30].iter().fold(100, |bal, t| bal + t);
println!("balance: {balance}");
let temps = [21.5, 25.25, 19.0];
let hottest = temps.iter().copied().fold(f64::MIN, f64::max);
println!("hottest: {hottest}");
}
Some("fig") Some("cherry")
10! = 3628800
balance: 160
hottest: 25.25
fold starts with a value (the accumulator), and for each item calls the closure with the accumulator and the item; whatever the closure returns becomes the new accumulator. sum, count and max could all be written with fold. The second fold passes the function f64::max instead of a closure.
| Consumer | Returns | Notes |
|---|---|---|
sum(), product() |
a number | give the result a type: let s: i32 = ... |
count() |
usize |
|
min(), max() |
Option<item> |
None when empty; items must be Ord (not f64) |
min_by_key(f), max_by_key(f) |
Option<item> |
compare by a key; on ties min_by_key returns the first, max_by_key the last |
min_by(f), max_by(f) |
Option<item> |
compare with a closure returning an Ordering; for f64 use a.total_cmp(b) |
fold(start, f) |
the accumulator | the general one |
any(f), all(f) |
bool |
stop at the first item that decides; all on nothing is true |
find(f) |
Option<item> |
the first item that matches; stops there |
position(f) |
Option<usize> |
the index of the first match |
last(), nth(n) |
Option<item> |
|
for_each(f) |
() |
run a side effect for each item |
any, all, find and position short-circuit: they stop pulling items as soon as they know the answer, which matters with long or infinite iterators.
Common mistakes
maxon floats.f64isn'tOrd, becauseNaNcan't be ordered:
fn main() {
let temps: Vec<f64> = vec![21.5, 19.0];
let hottest = temps.iter().max();
println!("{:?}", hottest);
}
error[E0277]: the trait bound `f64: Ord` is not satisfied
Use max_by(|a, b| a.total_cmp(b)), or fold(f64::MIN, f64::max) as above.
sumwithout a type.sumcan produce many types, so it needs to know which:
fn main() {
let v = vec![1, 2, 3];
let total = v.iter().sum();
println!("{total}");
}
error[E0283]: type annotations needed
Write let total: i32 = ..., or sum::<i32>() (the turbofish, next step).
unwrap()onmin/maxof a list that may be empty. Handle theNone, or return theOption.
Your task
The warehouse keeps a list of Items. Write three functions, each a single iterator chain (no loops):
fn total_units(items: &[Item]) -> u32: the sum of all quantities.fn out_of_stock(items: &[Item]) -> bool:trueif any item has quantity 0.fn priciest(items: &[Item]) -> Option<&str>: the name of the most expensive item, orNonefor an empty list. Prices aref64.
Challenge 2
Write two functions over a list of exam scores, each a single chain (no loops):
fn first_fail(scores: &[u32], pass_mark: u32) -> Option<usize>: the index of the first score belowpass_mark, orNoneif nobody failed.fn everyone_passed(scores: &[u32], pass_mark: u32) -> bool:trueif every score is at leastpass_mark(sotruefor an empty list).
Challenge 3
A habit tracker stores one bool per day: did you practice? Write fn longest_streak(days: &[bool]) -> usize that returns the longest run of true days in a row. Use fold with a tuple accumulator (current_run, best_run), no loops. For [true, true, false, true, true, true, false] the answer is 3.
Previous: Combining iterators: enumerate, zip, chain, flat_map, windows Next: collect, the turbofish, and collecting Results