Nugget 19
Home
v.iter(), v.iter_mut(), v.into_iter() —
three methods, three ownership stories. Picking the right one is the first
decision in every iterator pipeline.
You cannot call v.map(...) directly on a Vec. You
must first pick one of three iterator methods. Each answers a different
question:
// 1. iter() — "I just want to look at the values"
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
println!("{:?}", v); // ✅ v is still usable
// 2. iter_mut() — "I want to change the values in place"
let mut v = vec![1, 2, 3];
v.iter_mut().for_each(|x| *x *= 2);
println!("{:?}", v); // ✅ [2, 4, 6]
// 3. into_iter() — "I'm done with this Vec, give me owned values"
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.into_iter().map(|x| x * 2).collect();
// println!("{:?}", v); // ❌ v was consumed
| Method | Yields | Ownership | Use when |
|---|---|---|---|
.iter() |
&T |
Borrows (read) | Reading values, keep the Vec |
.iter_mut() |
&mut T |
Mutable borrow | Modifying elements in place |
.into_iter() |
T |
Consumes (move) | Transforming to a new collection |
.iter() — Read-Only AccessThe default choice. Borrows each element immutably — the original Vec stays available after the pipeline:
let numbers = vec![1, 2, 3, 4];
// Sum — doesn't need ownership
let sum: i32 = numbers.iter().sum();
// Filter — keeps references
let evens: Vec<&i32> = numbers.iter()
.filter(|x| *x % 2 == 0)
.collect();
// Map — each x is &i32
let doubled: Vec<i32> = numbers.iter()
.map(|x| x * 2) // x: &i32, but i32 is Copy so * happens auto
.collect();
println!("numbers is still here: {:?}", numbers); // ✅
💡 The for loop shortcut
for item in &v is equivalent to v.iter().
The & before v triggers the IntoIterator
impl for &Vec, which returns the same iterator.
.iter_mut() — Modify in Place
Each item is a mutable reference (&mut T). You can change
the elements without creating a new Vec:
let mut scores = vec![10, 20, 30];
// Add a bonus to every score
scores.iter_mut().for_each(|x| *x += 5);
println!("{:?}", scores); // [15, 25, 35]
// Double only evens using mutable map-like pattern
let mut nums = vec![1, 2, 3, 4];
nums.iter_mut().for_each(|x| {
if *x % 2 == 0 {
*x *= 10;
}
});
println!("{:?}", nums); // [1, 20, 3, 40]
// The Vec is still usable after — borrow has ended
println!("Original vector: {:?}", nums); // ✅
📐 iter_mut() + map
map with iter_mut() is awkward because map
wants to return values. for_each is usually the better fit for
in-place mutation — it exists just for side effects.
.into_iter() — Consume and TransformTakes ownership of each element. The original Vec is consumed — you can't use it after. This is perfect for one-shot transformations:
let strings = vec!["hello", "world"];
// Transform into owned Strings
let uppercased: Vec<String> = strings
.into_iter()
.map(|s| s.to_uppercase())
.collect();
println!("{:?}", uppercased); // ["HELLO", "WORLD"]
// println!("{:?}", strings); // ❌ consumed by into_iter()
let numbers = vec![1, 2, 3];
// into_iter yields T directly — no references to dereference
let doubled: Vec<i32> = numbers
.into_iter()
.map(|x| x * 2) // x is i32, clean and simple
.collect();
// Also the simplest for filter:
let v = vec![1, 2, 3];
let evens: Vec<i32> = v.into_iter()
.filter(|&x| x % 2 == 0) // filter receives &T, so |&x|
.collect();
💡 for loop equivalent
for item in v (no &) is equivalent to
v.into_iter() — it consumes the Vec. If you want to keep it,
write for item in &v instead.
Here's "double every element" done three ways depending on what you need afterward:
let numbers = vec![1, 2, 3]; // Keep original + produce new Vec (most common) let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); // numbers still usable ✅ let mut numbers = vec![1, 2, 3]; // Modify in place + keep the same Vec numbers.iter_mut().for_each(|x| *x *= 2); // numbers changed to [2, 4, 6] ✅ let numbers = vec![1, 2, 3]; // Consume original + produce new Vec let doubled: Vec<i32> = numbers.into_iter().map(|x| x * 2).collect(); // numbers gone — but no references to clean up ❌
iter() is the safest default. Drop down to into_iter()
when the ownership semantics are what you want (no dangling references, clean
move). Reach for iter_mut() only when you specifically want to
mutate in place.
📐 Why pros prefer the explicit methods
for item in &v works for simple reading, but the moment
you reach for map, filter, or collect,
the for loop fights you — you restructure into multiple statements.
Explicit iterators compose naturally: v.iter().map(...).filter(...).collect()
in one expression. They also make ownership visible at a glance —
.iter() says "borrowing", .into_iter() says "consuming" —
right there in the method name. Seasoned Rustaceans reach for them by habit,
not because for is wrong, but because iterators are more expressive
once you're comfortable with the model.
Because each method yields a different type, the closure patterns change (connecting to Nugget 18):
let v = vec![1, 2, 3]; // iter() yields &i32 → map gets &i32, filter gets &&i32 v.iter().map(|x| x * 2); // x: &i32 v.iter().filter(|&x| x > 1); // x: &i32 (destructured &) // iter_mut() yields &mut i32 → for_each gets &mut i32 v.iter_mut().for_each(|x| *x *= 2); // x: &mut i32, need *x // into_iter() yields i32 → map gets i32, filter gets &i32 v.into_iter().map(|x| x * 2); // x: i32 (clean!) // For filter, same rule as always: filter adds one & // v.into_iter().filter(|&x| x > 1); // x: i32
.iter() — borrows read-only, keep the Vec. Use by default..iter_mut() — mutable borrow, change in place. Use with for_each..into_iter() — consumes the Vec, you get owned values. Cleanest closure syntax.for item in &v = v.iter(), for item in v = v.into_iter().&T, &mut T, or T — which affects how you write your closures.