IntoIterator
IntoIterator
Level 6 — Closures & Functional Patterns Trait that allows a type to be used in
forloops.
1. Prerequisites
- Iterator — The trait that actually does the stepping (
.next()). for/ Range — The loop syntax that completely relies onIntoIteratorto function.- Trait — The system that defines this shared behavior.
2. Term Category
Rust-specific (loop conversion trait): std::iter::IntoIterator is the foundational conversion trait in Rust that enables types to be consumed by for loops. Collections like Vec<T> or HashMap<K, V> are not iterators themselves; instead, they implement IntoIterator to yield an iterator instance when .into_iter() is called or when passed to for item in collection.
3. Explanation
(1) Design Motivation — "Why did we design this?"
Writing for item in collection.into_iter() or for item in collection.iter() explicitly every time you loop over data would create repetitive boilerplate code.
Rust's IntoIterator trait solves this by providing syntactic desugaring for for loops:
// 1. What you write:
for item in collection { ... }
// 2. What rustc expands it to:
let mut iter = IntoIterator::into_iter(collection);
while let Some(item) = iter.next() { ... }
(2) The Three Variations of IntoIterator
Standard collections implement IntoIterator across three distinct value/borrow contexts:
impl<T> IntoIterator for Vec<T>: Consumes collection ownership and yields ownedItem = T.impl<'a, T> IntoIterator for &'a Vec<T>: Borrows collection immutably and yieldsItem = &'a T.impl<'a, T> IntoIterator for &'a mut Vec<T>: Borrows collection mutably and yieldsItem = &'a mut T.
(3) Reality Metaphor
Iterator: An automated ticket dispenser that dispenses one paper ticket at a time when you pull the lever (.next()).Vec(Collection): A sealed box containing 50 rolls of tickets. The box cannot dispense individual tickets on its own.IntoIterator: An automated unboxing machine (.into_iter()) that opens the sealed box and installs the ticket rolls inside the dispenser mechanism so tickets can be pulled one by one.
(4) Rust Code Examples
3-Way Iteration (T, &T, &mut T)
fn main() {
let mut numbers = vec![10, 20, 30];
// 1. Borrow immutably via &'a Vec<T>
for num in &numbers {
println!("Shared borrow: {num}");
}
// 2. Borrow mutably via &'a mut Vec<T>
for num in &mut numbers {
*num += 5;
}
// 3. Consume ownership via Vec<T>
for num in numbers {
println!("Owned value consumed: {num}");
}
// numbers is now moved and destroyed!
}
Implementing IntoIterator for a Custom Collection
struct UserDirectory {
users: Vec<String>,
}
impl IntoIterator for UserDirectory {
type Item = String;
type IntoIter = std::vec::IntoIter<String>;
fn into_iter(self) -> Self::IntoIter {
self.users.into_iter()
}
}
fn main() {
let dir = UserDirectory { users: vec!["Alice".into(), "Bob".into()] };
for user in dir { // Automatically calls UserDirectory::into_iter(dir)!
println!("User: {user}");
}
}
4. Common Mistakes & Pitfalls
Mistake 1: Unintentionally Consuming Collection Ownership in a for Loop
The mistake: Writing for item in collection when intending to inspect elements without destroying the collection.
Why it is wrong: for item in collection calls IntoIterator::into_iter(collection) on the owned value, moving collection ownership into the loop. Attempting to access collection afterward triggers compiler error E0382.
Incorrect:
let data = vec![1, 2, 3];
for x in data { println!("{x}"); }
// println!("Len: {}", data.len()); // ❌ Error E0382: use of moved value `data`
Fix:
let data = vec![1, 2, 3];
for x in &data { println!("{x}"); } // Borrow with &data!
println!("Len: {}", data.len()); // Correct!
Mistake 2: Implementing IntoIterator for Owned Type but Forgetting Reference Implementations
The mistake: Implementing IntoIterator for MyStruct without implementing IntoIterator for &'a MyStruct or &'a mut MyStruct.
Why it is wrong: Users writing for item in &my_struct will receive compile error E0277 because reference borrowing is not automatically generated for custom types.
Mistake 3: Confusing Iterator and IntoIterator in Generic Trait Bounds
The mistake: Specifying T: Iterator when accepting collections in generic functions.
Why it is wrong: Vec<T> implements IntoIterator, not Iterator. Constraining a generic parameter to T: Iterator rejects Vec or slices.
Incorrect:
fn process<I: Iterator<Item = i32>>(iter: I) { ... } // Rejects Vec<i32>!
Fix:
fn process<C: IntoIterator<Item = i32>>(container: C) { ... } // Accepts Vec<i32>!
5. Practice Exercises
Exercise 1: Custom Inventory Container with 3-Way IntoIterator
Scenario: Implement a warehouse inventory struct WarehouseInventory that implements IntoIterator for owned WarehouseInventory, shared reference &WarehouseInventory, and mutable reference &mut WarehouseInventory.
Requirements:
- Define struct
Item { pub name: String, pub count: u32 }. - Define struct
WarehouseInventory { pub items: Vec<Item> }. - Implement
IntoIteratorforWarehouseInventory,&'a WarehouseInventory, and&'a mut WarehouseInventory. - Write unit tests demonstrating all three loop forms.
Answer
Implementation
#[derive(Debug, PartialEq, Clone)]
pub struct Item {
pub name: String,
pub count: u32,
}
pub struct WarehouseInventory {
pub items: Vec<Item>,
}
// 1. Owned IntoIterator
impl IntoIterator for WarehouseInventory {
type Item = Item;
type IntoIter = std::vec::IntoIter<Item>;
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
// 2. Shared reference IntoIterator
impl<'a> IntoIterator for &'a WarehouseInventory {
type Item = &'a Item;
type IntoIter = std::slice::Iter<'a, Item>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter()
}
}
// 3. Mutable reference IntoIterator
impl<'a> IntoIterator for &'a mut WarehouseInventory {
type Item = &'a mut Item;
type IntoIter = std::slice::IterMut<'a, Item>;
fn into_iter(self) -> Self::IntoIter {
self.items.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_3way_into_iterator() {
let mut inv = WarehouseInventory {
items: vec![Item { name: "Widget".into(), count: 10 }],
};
// Shared reference loop
for item in &inv {
assert_eq!(item.count, 10);
}
// Mutable reference loop
for item in &mut inv {
item.count += 5;
}
// Owned loop
let items: Vec<Item> = inv.into_iter().collect();
assert_eq!(items[0].count, 15);
}
}
Technical Explanation
- Implementing
IntoIteratorforWarehouseInventoryenablesfor x in inv(owned). - Implementing
IntoIteratorfor&'a WarehouseInventoryenablesfor x in &inv(immutable borrow). - Implementing
IntoIteratorfor&'a mut WarehouseInventoryenablesfor x in &mut inv(mutable borrow).
Exercise 2: Zero-Copy Network Packet Slice Iterator
Scenario: Implement IntoIterator for a raw byte frame slice &'a PacketBuffer yielding borrowed header byte slices without copying memory.
Requirements:
- Define
struct PacketBuffer<'a> { pub raw: &'a [u8] }. - Implement
IntoIteratorfor&'a PacketBuffer<'a>returning byte references. - Write unit tests.
Answer
Implementation
pub struct PacketBuffer<'a> {
pub raw: &'a [u8],
}
impl<'a> IntoIterator for &'a PacketBuffer<'a> {
type Item = &'a u8;
type IntoIter = std::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.raw.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_packet_buffer_into_iter() {
let bytes = [0xDE, 0xAD, 0xBE, 0xEF];
let buf = PacketBuffer { raw: &bytes };
let mut collected = Vec::new();
for &b in &buf {
collected.push(b);
}
assert_eq!(collected, vec![0xDE, 0xAD, 0xBE, 0xEF]);
}
}
Technical Explanation
&'a PacketBuffer<'a>forwardsinto_itertoslice.iter().- Zero-copy iteration over internal byte slice buffers.
Exercise 3: Batch Processor Function with C: IntoIterator Bound
Scenario: Implement a generic function fn sum_positive<C>(collection: C) -> i32 where C: IntoIterator<Item = i32> that accepts any collection convertible to an iterator of integers.
Requirements:
- Generic parameter
C: IntoIterator<Item = i32>. - Sum positive integers.
- Write unit tests passing
Vec<i32>,Option<i32>, and ranges.
Answer
Implementation
pub fn sum_positive<C>(collection: C) -> i32
where
C: IntoIterator<Item = i32>,
{
collection.into_iter().filter(|&x| x > 0).sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generic_into_iterator_bound() {
// Works on Vec<i32>
assert_eq!(sum_positive(vec![-5, 10, 20, -1]), 30);
// Works on Ranges!
assert_eq!(sum_positive(-2..4), 1 + 2 + 3);
}
}
Technical Explanation
C: IntoIterator<Item = i32>enables callingsum_positivewithVec, array ranges, or custom collections.- Converts inputs into iterators lazily via
.into_iter().
6. Related Terms
- Iterator — The trait that
IntoIteratoractually produces. for/ Range — The loop syntax that relies entirely onIntoIteratorto function.VecDeque<T>— Related concept:VecDeque<T>.
7. Key Takeaways
IntoIteratoris a conversion trait providing.into_iter(self).for x in containeris syntax sugar forwhile let Some(x) = IntoIterator::into_iter(container).next().- Collections implement three forms of
IntoIterator: ownedcollection, shared borrow&collection, and mutable borrow&mut collection. - Use
C: IntoIterator<Item = T>as generic bounds for APIs accepting collections.