We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Collections, Text and First Iterators step 21 of 21
Everything you can collect into
Turn a list of (key, value) pairs into an index.
pub fn index_and_dedup(pairs: Vec<(String, i64)>) -> (Vec<String>, BTreeMap<String, i64>)
Return the distinct keys, sorted ascending, and a map from key to value.
When a key appears more than once, the map holds the last value seen —
[("a", 1), ("a", 9)] gives {"a": 9}, not {"a": 1}.
collect is not “make a Vec”
collect builds anything that implements FromIterator. That is a much
bigger set than most people use, and knowing it turns three-line loops into
one-liners:
iter.collect::<Vec<T>>()
iter.collect::<VecDeque<T>>()
iter.collect::<String>() // from char or &str items
iter.collect::<HashSet<T>>() // dedup, in one call
iter.collect::<BTreeSet<T>>() // dedup AND sort, in one call
iter.collect::<HashMap<K, V>>() // from (K, V) pairs
iter.collect::<BTreeMap<K, V>>() // ...ordered
iter.collect::<BinaryHeap<T>>()
and two that surprise everyone the first time:
// Iterator<Item = Result<T, E>> -> Result<Vec<T>, E>
let parsed: Result<Vec<i32>, _> = strings.iter().map(|s| s.parse()).collect();
// Iterator<Item = Option<T>> -> Option<Vec<T>>
let all: Option<Vec<T>> = maybe_items.collect();
The Result one short-circuits: the first Err stops the iteration and
becomes the whole answer, otherwise you get every Ok value in a Vec. That
single line replaces a loop with an early return, and you will use it
constantly once the errors track begins.
Collecting into a map: the last-wins rule
let map: BTreeMap<String, i64> = pairs.into_iter().collect();
FromIterator for both map types is defined in terms of insert, and
insert overwrites. So duplicate keys are silent data loss, and the
survivor is the last one. That is the opposite of entry().or_insert(v),
which keeps the first.
Neither is wrong; they are different operations, and this problem specifies
the collect one. What is dangerous is not knowing which you wrote — the
test case with a repeated key exists so that you find out here rather than in
production.
::: question HashSet::insert returns whether the value was new, and HashMap::insert returns the displaced value. Why does collect throw both away?
Because FromIterator has one job — turn a stream of items into a
collection — and it has to work identically for every collection. There is no
return channel in collect for “and by the way, three of these were
duplicates”.
If you need to know, do not use collect. Write the loop and look at what
insert gives you back:
for (k, v) in pairs {
if let Some(old) = map.insert(k.clone(), v) {
eprintln!("duplicate key {k}: {old} replaced by {v}");
}
}
This is a small, general lesson about combinators: they are compact because they discard the information you usually do not want. When you do want it, drop back to the loop rather than trying to smuggle it out. :::
Order, and the trap the harness will catch
Collecting into a HashSet is the shortest correct dedup in the language.
But HashSet iteration order is randomised per run — the same
randomisation you met earlier in this track — so:
let keys: Vec<String> = pairs.iter().map(|(k, _)| k.clone())
.collect::<HashSet<_>>()
.into_iter()
.collect(); // deduped, and in a different order every run
is a test that passes on your machine and fails on the grader. Two fixes:
-
route through a
BTreeSetinstead — dedup and sort in one step; -
or sort the
Vecexplicitly afterwards.
For this problem there is a third and shorter route: you are already building
a BTreeMap, whose keys() are unique and in order by construction.
map.keys().cloned().collect() is the whole thing.
The two errors you will meet
E0283, “type annotations needed.” collect is generic over its output,
and when nothing in the surrounding code pins it down the compiler refuses to
guess:
error[E0283]: type annotations needed
= note: cannot satisfy `_: FromIterator<i32>`
help: consider giving `c` an explicit type
Fix it with a binding annotation (let v: Vec<_> = ...) or the turbofish
(.collect::<Vec<_>>()). Prefer the annotation when the type is the
interesting part of the line, and the turbofish when it is incidental.
E0277, which is what the starter gives you: the iterator’s item type and the destination disagree.
error[E0277]: a value of type `Vec<String>` cannot be built from an
iterator over elements of type `&String`
pairs.iter().map(|(k, _)| k) yields &String, and a Vec<String> needs
owned values. .cloned() is the fix and the allocation is the honest price of
the owned return type. This is the same message you have now seen from set
operations and from BTreeMap::range — once you recognise it, it is a
two-second diagnosis for the rest of your Rust life.
One more thing the starter gets wrong
Even after the compile error is fixed, the starter’s map is built with
entry(..).or_insert(v), which keeps the first value. The specification
says last. Fixing that is the difference between code that compiles and code
that is correct, and only the test cases can tell you.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.