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 outputs from the same input numbers:
pub fn shapes(nums: Vec<i64>) -> (Vec<i64>, String, Vec<Vec<i64>>)
- The first output contains each number multiplied by two.
- The second contains one ASCII digit per number: its last decimal digit,
ignoring its sign. For example,
12,-7and5contribute275. - The third contains the original numbers in groups of three. Keep a final group of one or two numbers when the length is not divisible by three.
Preserve input order and duplicates in all outputs. Empty input returns
([], "", []); it does not produce an empty inner group. Inputs are in
i64::MIN / 2 ..= i64::MAX / 2, so every doubled value fits in i64.
[12, -7, 5] -> ([24, -14, 10], "275", [[12, -7, 5]])
[1, 2, 3, 4] -> ([2, 4, 6, 8], "1234", [[1, 2, 3], [4]])
Choose what collect builds
collect consumes an iterator and builds a result whose type implements
FromIterator for the iterator's items. The items constrain the choices,
but do not always select one result type. Characters, for example, can be
collected into either a String or a Vec<char>.
The target type can come from a binding annotation, explicit generic arguments or surrounding context such as a function's return type. Here are all three:
fn doubled(values: &[i64]) -> Vec<i64> {
values.iter().map(|n| n * 2).collect()
}
fn main() {
let values = [1i64, 2, 3];
let annotated: Vec<i64> = values.iter().map(|n| n * 2).collect();
let explicit = values.iter().map(|n| n * 2).collect::<Vec<_>>();
assert_eq!(annotated, explicit);
assert_eq!(doubled(&values), [2, 4, 6]);
}
The syntax ::<...> is commonly called the turbofish. In collect::<Vec<_>>(),
Vec chooses the destination and _ asks Rust to infer its element type.
That works when the remaining constraints determine the element type.
An explicit Vec<i64> is also useful when you want to state that requirement;
the shortest annotation is not always the clearest one.
Why the starter is ambiguous
The starter collects characters into digit_chars without selecting its type,
then passes that value to String::from_iter. This still leaves multiple
possible intermediate collections, so the compiler reports E0283.
Choose an intermediate type, or avoid the intermediate collection and collect
the characters directly into a String. The latter is a simpler route here.
The other collections can obtain their types from the returned tuple, though
annotations can make the code easier to read.
E0282 and E0283 both concern insufficient type information. Their diagnostic notes describe different inference problems; they do not imply fundamentally different repair strategies. Read the expression and required trait bounds, then provide the missing type information where it is most helpful.
One character for each last digit
Use n.unsigned_abs() % 10 to obtain a digit magnitude in 0..=9. Convert it
to an ASCII digit with char::from(b'0' + digit as u8). The cast is safe here
because the remainder is at most nine.
The magnitude operation also works at i64::MIN, unlike signed abs().
This standalone example demonstrates that property; i64::MIN itself is
outside this problem's doubling contract:
fn main() {
let values = [12i64, -7, 100, i64::MIN];
let digits: String = values.iter()
.map(|n| char::from(b'0' + (n.unsigned_abs() % 10) as u8))
.collect();
assert_eq!(digits, "2708");
}
Turn borrowed chunks into owned groups
nums.chunks(3) yields borrowed slices. Map each slice to an owned vector with
to_vec(), then collect those vectors. For these i64 elements, this copies
the values into the new groups.
fn main() {
let values = [1i64, 2, 3, 4];
let groups: Vec<Vec<i64>> = values.chunks(3).map(|chunk| chunk.to_vec()).collect();
assert_eq!(groups, [vec![1, 2, 3], vec![4]]);
}
A positive chunk size produces nonempty chunks, with no chunks for an empty
slice. A zero chunk size panics. chunks_exact(3) only yields full groups,
retaining a short remainder for separate access; it would miss this task's
final group unless you handled that remainder explicitly.
For a slice, Clippy's iter_cloned_collect lint can suggest to_vec() instead
of .iter().cloned().collect(). Here, to_vec() directly expresses the desired
conversion from a borrowed chunk to an owned group.
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.