Skip to content

← Closures and Iterators step 6 of 28

Medium Primitives

Sum collections and iterators with IntoIterator

Complete one generic summing function and a dispatcher that supplies five kinds of input. Fix the starter's trait bound and implement its three missing branches.

pub fn sum_all<I>(src: I) -> i64
where
    I: IntoIterator<Item = i64>;

pub fn dispatch(kind: String, data: Vec<i64>) -> i64

The signatures above describe the required API. sum_all consumes its input sequence and returns its sum, or zero for an empty sequence. dispatch must construct and pass the following inputs to that function:

Exact kind Input passed to sum_all Values included
vec The owned Vec<i64> All entries, including duplicates
slice data.iter().copied() All entries, including duplicates
array An owned [i64; 4] First four entries, padded with zeros if shorter
deque A VecDeque<i64> made from data All entries, including duplicates
set A BTreeSet<i64> made from data Each distinct value once, in ascending order

An unknown kind returns -1. Matching is case-sensitive and does not trim whitespace. Empty data returns zero for every recognized kind. Inputs are chosen so intermediate additions and the final sum fit in i64; wrapping or saturating sums are not required.

For data [1, 2, 3, 4, 5], array returns 10 and the other kinds return 15. For [5, 5, 5], set returns 5 and the other kinds return 15. The fixed array length and set deduplication are intentional parts of the task.

An iterable value is not necessarily an iterator

Iterator provides operations such as next and sum. IntoIterator describes how a value becomes an iterator. A Vec implements IntoIterator, so it can be consumed to produce an iterator; the Vec itself does not implement Iterator. That is why the starter's call sum_all(data) fails with E0277.

Change the bound and convert src with into_iter before summing. Existing iterators also implement IntoIterator, returning themselves, so the same function accepts the copied iterator used by the slice branch. The Item = i64 requirement still matters: an iterator yielding &i64 does not satisfy it.

IntoIterator is useful for this consume-a-sequence API. Iterator can be the appropriate bound when an API needs to operate on an iterator directly; the broader trait is not a universal replacement for it.

See the IntoIterator documentation.

What a for loop consumes

A for loop converts the expression after in using IntoIterator, then repeatedly calls next until it returns None. The expression's type determines the item:

Expression after in Item for a vector of T Ownership effect
v T Moves the vector into iteration
&v &T Borrows the vector's elements
&mut v &mut T Mutably borrows the elements

For example, these loops first mutate through references and then consume the vector:

fn main() {
    let mut values = vec![1_i64, 2, 3];
    for value in &mut values {
        *value += 1;
    }
    let mut total = 0;
    for value in values {
        total += value;
    }
    assert_eq!(total, 9);
}

This describes the iteration behavior, not the complete compiler expansion, which also handles temporary lifetimes and control flow. See the for-loop reference.

Build each branch explicitly

Start the array as [0_i64; 4]. Pair its mutable slots with data using zip; iteration stops when either side ends. Extra values are discarded and unused slots remain zero. Passing the array by value supplies i64 items on the exercise's Rust 2024 edition.

Use VecDeque::from(data) for deque and collect into BTreeSet<i64> for set. A type annotation or collect's type argument can identify the target collection. For slice, copied converts the shared i64 references from iter into i64 values.

Passing requires compilation, tests and Clippy with warnings denied.