198. into_iter() to Transform — Move Owned Items Instead of cloned()
You have a Vec you’re about to throw away, and you want a transformed one. Reaching for iter().cloned() (or iter().map(|x| x.clone())) duplicates every element on the way out — but you owned them already. into_iter() moves them straight through.
This is the afternoon half of this morning’s mem::replace bite: both are about moving owned data forward instead of copying it. There, it was an enum behind &mut self; here, it’s the elements of a collection.
The trap: iter().cloned() on a collection you’re discarding
You want to uppercase a list of names. The list is a local you won’t touch again:
| |
That one’s not even the worst case — to_uppercase builds a new String regardless. The real waste shows up when the transform keeps the value and you clone just to own it:
| |
Every s.clone() heap-allocates a duplicate of a string you were about to drop. The original names gets freed on return — you paid to copy bytes that were headed for the incinerator.
The fix: into_iter() consumes the collection and hands you owned items
into_iter() on a Vec<String> yields String by value, not &String. The transform now moves each element — no clone, no second allocation:
| |
| |
Each string’s heap buffer is threaded through by pointer. Zero element copies.
The rule of thumb
If you still need the collection afterward, iter() (borrow) is correct — you can’t move out of something you’re keeping. But the moment the collection is yours to consume and you don’t need it again, into_iter() skips a copy of every element. A for x in v loop already does this (it’s into_iter under the hood); the win is remembering that .map, .filter, and friends can start from into_iter() too.
| |
cloned() earns its keep when you genuinely need both the original and a copy. When you don’t, it’s a tax on data you’re about to free.