We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 21 of 28
collect and the turbofish: three targets, one source
Build three different collections from the same numbers.
pub fn shapes(nums: Vec<i64>) -> (Vec<i64>, String, Vec<Vec<i64>>)
-
.0— every number doubled, as aVec<i64>. -
.1— aStringmade of each number’s last decimal digit, in order, ignoring the sign.[12, -7, 5]gives"275". -
.2— the original numbers in chunks of three, in order; the last chunk may be shorter.
[12, -7, 5] -> ([24, -14, 10], "275", [[12, -7, 5]])
[1, 2, 3, 4] -> ([2, 4, 6, 8], "1234", [[1, 2, 3], [4]])
[] -> ([], "", [])
collect runs backwards
Every other method you have met takes its meaning from what is on the left of
the dot. collect takes its meaning from what is on the right of the
equals sign — from the type you are assigning into.
pub fn collect<B: FromIterator<Self::Item>>(self) -> B
B appears only in the return type. Nothing about the iterator picks it.
So collect is not “make a Vec”; it is “make whatever the context asked for,
provided that thing knows how to be built from these items”. With
Item = char alone, the candidates in alloc include String, Box<str>
and Vec<char>; with Item = (K, V), HashMap<K, V> and BTreeMap<K, V>;
with Item = Result<T, E>, the wonderful Result<Vec<T>, E> that stops at
the first error.
This is return-type-driven inference. Outside Rust and Haskell it is rare, and until someone explains the mechanism the error looks like a compiler malfunction.
The error, and which code it is
The starter collects into a binding whose type nothing determines:
error[E0283]: type annotations needed
|
4 | let digit_chars = nums
| ^^^^^^^^^^^
...
7 | .collect();
| ------- type must be known at this point
|
= note: multiple `impl`s satisfying `_: FromIterator<char>` found in the `alloc` crate:
- impl FromIterator<char> for Box<str>;
- impl FromIterator<char> for ByteString;
- impl FromIterator<char> for String;
help: consider giving `digit_chars` an explicit type
Note the code: E0283, not E0282. The difference matters and is worth learning, because the fix is different.
- E0282 is “I have no information at all”. Nothing constrains the type.
-
E0283 is “I have several candidates and no way to choose”. That is
what happened here — the compiler knows the items are
charand can list three types that accept them.
When you see the “multiple impls satisfying” note, you are being asked to
pick, not to inform.
Three ways to say it, and which to prefer
let v: Vec<i64> = it.collect(); // annotate the binding
let v = it.collect::<Vec<i64>>(); // turbofish
fn f() -> Vec<i64> { it.collect() } // let the return type say it
All three work. The habit worth building is the minimal annotation:
it.collect::<Vec<_>>()
The _ says “you work out the element type” — which the compiler always can,
from the iterator — while the Vec says “this is the container”, which it
cannot. Writing collect::<Vec<i64>>() is not wrong, but it duplicates
information that already exists and will need editing when the element type
changes.
The name turbofish is for ::<>, and it exists because f<T>(x) would be
ambiguous with f < T > (x) in an expression. The :: disambiguates. Once
you know that, the syntax stops looking arbitrary.
In this problem
Three collects, three different targets, and the third one nests: a
Vec<Vec<i64>> built from slice::chunks, whose items are &[i64] and
therefore need to_vec() before they can go in. chunks(n) never yields an
empty chunk and never panics for n > 0; the final chunk is short if the
length does not divide evenly. (chunks_exact drops the remainder instead,
and hands it to you separately via remainder().)
For the digit character: n.unsigned_abs() gives the magnitude as a u64
without the i64::MIN overflow that abs() would have, % 10 takes the
last digit, and char::from(b'0' + d) turns 0..=9 into '0'..='9'.
Two lints to know here
-
iter_cloned_collect—.iter().cloned().collect()on a slice is.to_vec(). Default-on. -
from_iter_instead_of_collect—String::from_iter(it)isit.collect::<String>(). This one ispedantic, so it will not fire here, but the starter is written in exactly that style and the postfix form reads better in a chain.
Grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.