RustArena

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

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.

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).

Your task

The warehouse keeps a list of Items. Write three functions, each a single iterator chain (no loops):

Challenge 2

Write two functions over a list of exam scores, each a single chain (no loops):

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