We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 5 of 28
iter, iter_mut, into_iter: all three, one Vec
One input Vec<String>. Three answers, in this order, from that one vector.
pub fn triple(words: Vec<String>) -> (usize, Vec<usize>, Vec<String>)
-
.0— the total byte length of the words exactly as they arrived, computed without consuming or modifying the vector. -
.1— uppercase every word in place, and return each word’s new byte length, in the original order. -
.2— the same (now uppercased) words, owned, in reverse order.
For ["hello", "world"] that is (10, [5, 5], ["WORLD", "HELLO"]).
For [] it is (0, [], []).
The order is not decoration. It is the point. Step 1 must read without touching, step 2 must mutate through the vector you still own, and step 3 must take the strings out. Each step forces a different iteration form, and no other choice compiles.
The three forms
Every collection in std offers the same three, and they differ in exactly
one thing: what Item is.
| Call |
Item for a Vec<String> |
You get | The Vec afterwards |
|---|---|---|---|
v.iter() |
&String |
read-only views | still yours, untouched |
v.iter_mut() |
&mut String |
exclusive views | still yours, modified |
v.into_iter() |
String |
the values themselves | gone |
Almost every confusing iterator error you will hit for the next three tracks
is a symptom of picking the wrong one of these. A closure parameter that
turns out to be &&i32; E0507 cannot move out of a shared reference;
E0277 from collect saying a Vec<String> cannot be built from an
iterator over &String. All the same root cause.
So learn to ask the question in the other direction: do I need to read it, change it, or take it? The answer picks the method, and the method fixes the types.
The starter does not compile, and its error is the lesson
It opens with the obvious loop:
for w in words {
total += w.len();
}
and then tries to use words again. Compile it and read:
error[E0382]: borrow of moved value: `words`
for w in words desugars to IntoIterator::into_iter(words), which takes
the vector by value. It is not a loop that reads your vector; it is a
loop that eats it. w is a String, not a &String, precisely because
the vector gave it away. After the loop, words is a moved-out local and
nothing may touch it.
Two ways to fix that line, and they are the same fix written twice:
for w in &words or words.iter(). Item 9.6 shows why those are literally
identical after desugaring, and clippy has a default-on lint,
into_iter_on_ref, whose entire job is to tell you that (&v).into_iter()
and v.iter() are the same call.
Mutating in place needs two muts, and they are different
Step 2 wants iter_mut, which is &mut self, which means the binding must
be mutable. Your parameter is words: Vec<String> — you own it, but the
binding is not mut, so you will meet:
error[E0596]: cannot borrow `words` as mutable, as it is not declared as mutable
Write mut words: Vec<String> in the signature. This is worth pausing on:
mut on a parameter is not part of the function’s type. Callers cannot
see it, are not affected by it, and do not need to change. It is a property
of your local binding, exactly like let mut. You are allowed to make a
value you own mutable; that is what owning it means.
Inside the closure, w is &mut String. To replace the string you must
assign through the reference: *w = w.to_uppercase();. Forgetting the * is
E0308 — you would be assigning a String to a &mut String.
Uppercasing is not a per-byte operation
str::to_uppercase is full Unicode uppercase, and it can change the length.
The hidden cases exercise this deliberately:
-
"fire"is 5 bytes (thefiligature is one 3-bytechar); uppercased it becomes"FIRE", which is 4 bytes. -
"straße"is 7 bytes;ßuppercases toSS, so"STRASSE"is 7 bytes — same length, different character count.
That is why the problem asks for the byte total before and the per-word lengths after: if you compute both from the same snapshot you get the wrong answer with no compile error at all.
Do not reach for clone
There is a version of this that “works”: clone the vector twice and use
into_iter three times. Do not. Every phase here is expressible without a
single allocation beyond the uppercased strings themselves, and the whole
reason the three forms exist is so you never have to pay for a copy to
satisfy the borrow checker. If you find yourself cloning to get past a
borrow error, the error was telling you to pick a different iteration form.
Grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.