We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 6 of 28
IntoIterator: one bound that accepts five containers
Write one generic summing function, then feed it five different containers without changing it.
pub fn sum_all<I>(src: I) -> i64
where
I: IntoIterator<Item = i64>;
pub fn dispatch(kind: String, data: Vec<i64>) -> i64
dispatch builds a container from data according to kind and hands it to
sum_all:
kind |
what to pass |
|---|---|
"vec" |
the Vec<i64> itself |
"slice" |
data.iter().copied() — an iterator, not a container |
"array" |
a [i64; 4] holding the first four entries, zero-padded |
"deque" |
a VecDeque<i64> built from data |
"set" |
a BTreeSet<i64> built from data |
Anything else returns -1.
Two of those rows are deliberately not sum-preserving, and the hidden cases
check both: an array has a fixed length, so entries past the fourth are
dropped; and a BTreeSet deduplicates, so [5, 5, 5] sums to 5. For
input with at most four entries and no duplicates, all five kinds agree —
which is the point of the exercise.
The starter compiles nothing, and its error is the lesson
The starter declares the bound as I: Iterator<Item = i64>, which is the
bound almost everyone writes first. Compile it:
error[E0277]: `Vec<i64>` is not an iterator
Correct. A Vec is not an iterator. It has never been one. Neither is an
array, a VecDeque, a BTreeSet, a HashMap, a String, or a Range
— wait, a Range actually is one, and we will come back to that.
A Vec is a thing that can produce an iterator. That capability is a
separate trait:
pub trait IntoIterator {
type Item;
type IntoIter: Iterator<Item = Self::Item>;
fn into_iter(self) -> Self::IntoIter;
}
Change the bound to IntoIterator<Item = i64>, call .into_iter() on src
inside the body, and all five arms compile against the same signature.
How for actually works
There is no for loop in Rust’s semantics. There is a desugaring:
for x in expr {
body
}
becomes, near enough,
let mut it = IntoIterator::into_iter(expr);
while let Some(x) = it.next() {
body
}
That single line explains the three forms you have been memorising. std
provides three separate impls for Vec<T>, and the one that gets picked
depends only on what you wrote after in:
| You write | Impl selected |
Item |
|---|---|---|
for x in v |
impl IntoIterator for Vec<T> |
T — the values, v is consumed |
for x in &v |
impl IntoIterator for &Vec<T> |
&T |
for x in &mut v |
impl IntoIterator for &mut Vec<T> |
&mut T |
So for x in &v gives you references not because for is being helpful, but
because &Vec<T> is a different type with a different impl whose
associated Item is &T. And v.iter() is simply a shorter way to name the
second one — the standard library’s iter() is defined as calling
(&self).into_iter(). Clippy has a default-on lint, into_iter_on_ref,
that tells you so when you write it the long way.
There is also a blanket impl impl<I: Iterator> IntoIterator for I, whose
into_iter just returns self. Every iterator is trivially into-iterable.
That blanket impl is why I: IntoIterator is strictly more permissive
than I: Iterator — it accepts every iterator plus every container. There
is no cost and no downside. This is why essentially every generic function in
std that consumes a sequence — Extend::extend, FromIterator,
Iterator::chain, Iterator::zip — takes IntoIterator, not Iterator.
Copy the habit. Accepting Iterator when you meant IntoIterator forces
every caller to write .iter() for no reason, and forbids the ones holding a
Vec from passing it at all.
The array row is not filler
[i64; 4] is a distinct type from [i64; 5] — the length is part of the
type. That is why you cannot build one from a runtime-length Vec without
deciding what to do about the mismatch, and why this problem specifies
zero-padding and truncation rather than leaving it to you.
Arrays gained a by-value IntoIterator impl in Rust 1.53, and edition 2021
changed array.into_iter() in method position to yield values rather than
references. In edition 2024 — which is what compiles here — for x in arr
gives you i64, not &i64. If you find a tutorial claiming otherwise, it
predates that change.
Building the containers
VecDeque has a From<Vec<T>> impl that reuses the allocation. BTreeSet
does not; you collect into it, which needs a turbofish or a typed binding
so inference knows the target — item 9.21 is about exactly that. And
.iter().copied() turns &i64 into i64, which is item 9.7.
Grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.