Iterators
Owned vs. Borrowed Iteration
Choosing between borrowed iteration (.iter() / .iter_mut()) and owned iteration (.into_iter()) determines whether the original collection survives the loop and whether elements are accessed by reference or moved by value.
Side-by-Side Comparison
1. Shared Borrow: vec.iter() ──> yields &T (vec remains usable)
2. Mutable Borrow: vec.iter_mut() ──> yields &mut T (vec remains usable, modified)
3. Owned Move: vec.into_iter() ──> yields T (vec is consumed and freed)
| Iteration Mode | Method / for Syntax |
Item Type on Vec<T> |
Collection After Iteration | Best Used When... |
|---|---|---|---|---|
| Shared borrow | vec.iter() or for x in &vec |
&T |
Unchanged and accessible | Inspecting, searching, or computing aggregates while keeping vec |
| Mutable borrow | vec.iter_mut() or for x in &mut vec |
&mut T |
Modified in place and accessible | Updating elements in place without reallocating |
| Owned (by value) | vec.into_iter() or for x in vec |
T |
Consumed (moved) | Transforming a collection into a new collection and avoiding .clone() |
Avoiding Unnecessary .clone() Calls with .into_iter()
If a collection of heap-allocated items (Vec<String>) is no longer needed after transforming it, using .into_iter() moves each String into the new collection in $O(1)$ pointer-copy time instead of allocating new heap buffers with .iter().map(|s| s.clone()):
#[derive(Debug)]
struct User {
username: String,
active: bool,
}
fn extract_active_usernames(users: Vec<User>) -> Vec<String> {
// `.into_iter()` moves `user.username` directly into the output `Vec<String>`
// without cloning a single String:
users
.into_iter()
.filter(|user| user.active)
.map(|user| user.username)
.collect()
}
fn main() {
let roster = vec![
User { username: String::from("alice"), active: true },
User { username: String::from("bob"), active: false },
User { username: String::from("carol"), active: true },
];
let active_names = extract_active_usernames(roster);
println!("Active: {active_names:?}");
}
Active: ["alice", "carol"]
Partial Consumption While Keeping the Container (Vec::drain)
When you need owned elements (T) like .into_iter(), but you want to keep the Vec<T> allocation alive for reuse (or only remove a subrange), use vec.drain(range):
fn main() {
let mut queue = vec![
String::from("job-1"),
String::from("job-2"),
String::from("job-3"),
];
// Moves the first 2 Strings out by value while keeping `queue` valid:
let batch: Vec<String> = queue.drain(0..2).collect();
println!("Drained batch: {batch:?}");
println!("Remaining queue: {queue:?} (capacity: {})", queue.capacity());
}
Drained batch: ["job-1", "job-2"]
Remaining queue: ["job-3"] (capacity: 3)