July 14, 20253 min

Implications of iterating over a Vec with .into_iter() instead of .iter()

m
mayo

Key Differences

.into_iter() .iter()
Consumes the Vec (takes ownership). Borrows the Vec immutably.
Yields owned values (T). Yields references (&T).
Original Vec is unusable afterward. Original Vec remains intact.

When to Use .into_iter()

Need Ownership of Elements

Useful when you want to move elements out of the Vec (e.g., transferring to another collection):

let vec = vec![String::from("a"), String::from("b")];
let new_vec: Vec<String> = vec.into_iter().collect();  // `vec` is consumed
// println!("{:?}", vec);  // ERROR: `vec` moved

Destructive Operations

For operations that destroy the Vec (e.g., sorting and deduplicating in one pass):

let mut vec = vec![3, 1, 2, 1];
vec = vec.into_iter().unique().sorted().collect();  // Destructive but efficient

Performance Optimization

Avoids cloning when working with owned data (e.g., Vec):

let vec = vec![String::from("rust")];
for s in vec.into_iter() {  // No clone, moves `String`
    println!("{}", s);
}

Ownership Implications

After .into_iter(), the original Vec is moved and can't be used:

let vec = vec![1, 2, 3];
let iter = vec.into_iter();  // `vec` is moved here
// println!("{:?}", vec);    // ERROR: value borrowed after move

Works with non-Copy types (e.g., String, Box):

let vec = vec![String::from("hello")];
let s = vec.into_iter().next().unwrap();  // Moves the `String` out

Comparison with .iter()

Scenario .into_iter() .iter()
Need to reuse the Vec ❌ No ✅ Yes
Modify elements ❌ No (consumed) ✅ Yes (iter_mut())
Avoid cloning owned data ✅ Yes ❌ No (requires clone())

Real-World Examples

Transferring Data

Moving a Vec into a function that takes ownership:

fn process(data: impl Iterator<Item = String>) { /* ... */ }
let vec = vec![String::from("a"), String::from("b")];
process(vec.into_iter());  // Efficient, no clones

Destructive Filtering

Remove elements while iterating:

let vec = vec![1, 2, 3, 4];
let evens: Vec<_> = vec.into_iter().filter(|x| x % 2 == 0).collect();

Performance Considerations

  • Zero-cost for primitives (i32, bool): .into_iter() and .iter() compile to the same assembly if T: Copy.
  • Avoids allocations when chaining adapters (e.g., .map().filter()).

Key Takeaways

Use .into_iter() to:

  • Move elements out of a Vec.
  • Optimize performance with owned data.
  • Destructively transform collections.

🚫 Avoid if you need to:

  • Reuse the Vec after iteration.
  • Share references across threads (&T is Sync; T might not be).

Try This: What happens if you call .into_iter() on a Vec and then try to use the original Vec in a parallel iterator (e.g., rayon::iter)?

Answer: Compile-time error! The Vec is already consumed. Use .par_iter() instead for parallel read-only access.