We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 12 of 28
zip and enumerate: pair two lists and report the leftovers
Pair names with scores by position, then report how many entries did not get a partner.
pub fn interleave_report(names: Vec<String>, scores: Vec<i64>) -> Vec<String>
The contract
- Pair
names[0]withscores[0],names[1]withscores[1], and so on, stopping as soon as either list has no more entries. The number of pairs is the length of the shorter list. - For each pair, in input order, emit one line formatted
"idx:name=score".idxis the pair's position counted from 1,nameis copied unchanged (it may be empty or contain any Unicode text), andscoreis written in ordinary decimal, with a leading-when negative. - After the pair lines, emit exactly one more line,
"unmatched:K".Kis the number of entries in the longer list that were not paired, which is the difference between the two lengths. This line is present in every result, including whenKis0and when both lists are empty.
names = ["a", "b", "c"]
scores = [10]
-> ["1:a=10", "unmatched:2"]
names = ["a"]
scores = [10, 20, 30]
-> ["1:a=10", "unmatched:2"]
names = []
scores = []
-> ["unmatched:0"]
In the first example only one pair exists, and "b" and "c" are left over.
In the second, the longer list is scores, and 20 and 30 are left over.
zip stops at the shorter input without reporting it
An iterator adapter is a method that wraps an iterator and produces a new
one. zip combines two iterators into one that yields pairs:
fn zip<U: IntoIterator>(self, other: U) -> Zip<Self, U::IntoIter>
Each call to the zipped iterator's next first advances the left iterator.
If that returns None, the zipped iterator returns None without touching
the right one. Otherwise it advances the right iterator, and returns
Some((left, right)) only if that also produced a value. So iteration ends
at the shorter input. zip returns no error and adds no padding, and the
zipped iterator has no method that reports how many items the longer side
still had.
Often that truncation is what you want. When the mismatch matters, as it does here, compute it separately. The lengths are enough:
let leftover = names.len().abs_diff(scores.len());
abs_diff returns the absolute difference of two integers, so the argument
order does not matter. Compare the starter's names.len() - scores.len().
len() returns usize, an unsigned type, so that subtraction has no
correct result when scores is longer. What happens then depends on how the
program is compiled. In a debug build, overflow checks are on and the
program panics with attempt to subtract with overflow. The grading runtime
compiles with rustc -O, which turns overflow checks off unless they are
requested explicitly, so the subtraction wraps around instead. On a 64-bit
target, 1 - 3 becomes usize::MAX - 1, and the starter reports:
unmatched:18446744073709551614
When subtracting two lengths, either know which one is larger or use
abs_diff.
enumerate numbers the items it receives
fn enumerate(self) -> Enumerate<Self> // Item = (usize, Self::Item)
enumerate pairs each item with a counter that starts at 0. It counts the
items that reach it, not positions in the original data. If you place it
after a filter, it numbers the items that passed the filter; if you place
it before, it numbers the original positions. Both can be correct, so choose
the order on purpose.
The counter cannot be told to start at 1. This exercise needs 1-based
numbers, so either add one where you format (i + 1), or zip with an
unbounded range instead of enumerating: (1..).zip(...) yields
(1, item), (2, item), and so on.
Where you put enumerate relative to zip changes the shape of each item:
a.iter().enumerate().zip(b) // Item = ((usize, &A), B::Item)
a.iter().zip(b).enumerate() // Item = (usize, (&A, B::Item))
Both yield the same values. The second shape destructures as
|(i, (name, score))|, which reads naturally for this exercise.
What Clippy reports on the starter
Clippy is Rust's linter. This exercise is graded by compiling, running the
tests, and running Clippy with -D warnings, which turns every warning into
an error. On the starter, Clippy 1.98 reports:
error: the variable `idx` is used as a loop counter
help: consider using: `for (idx, i) in (1..).zip((0..pairs))`
= note: `-D clippy::explicit-counter-loop` implied by `-D warnings`
explicit_counter_loop fires when a loop increments its own counter
alongside the loop variable. A related lint, needless_range_loop, fires on
for i in 0..v.len() when i is used only to index v, and suggests
iterating over v directly. Both lints are warnings by default. They do not
forbid index loops in general, but zip and enumerate cover the common
case of walking sequences in step. Iterating also removes the explicit
indexing, so there is no names[i] that could panic on an out-of-range
index. Fixing the lint alone does not fix the subtraction, which Clippy does
not report here.
Related adapters
chain(other)yields every item of the first iterator, then every item of the second. Both must produce the sameItemtype.rev()iterates in reverse. It requiresDoubleEndedIterator, which slice andVeciterators and bounded integer ranges such as0..nimplement.HashMapiterators and unbounded ranges such as0..do not.cycle()repeats an iterator from the start forever. It requires the iterator to beClone. Unless the underlying iterator is empty, the result never ends, so limit it withtakeor by zipping it with a finite iterator. Clippy'sinfinite_iterlint, which is an error by default, catches some obviously unbounded uses, such as collecting acycle().
If you need padding instead of truncation, chain a repeated filler onto the
shorter side, for example scores.iter().copied().chain(std::iter::repeat(0)),
and zip the longer side with that. cycle would repeat the shorter list's
own values instead, which is a different result.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.