Ownership and Iterator Invalidation
In languages without compile-time borrow checking (such as C++), mutating a vector while iterating over it or holding a pointer to an element causes iterator invalidation—a silent use-after-free bug if the mutation reallocates the vector's buffer. Rust's ownership and borrowing rules make iterator invalidation impossible in safe code.
How the Borrow Checker Prevents Iterator Invalidation (E0502)
Calling v.iter() borrows v immutably (&'a Vec<T>) for the entire lifetime 'a of the Iter<'a, T> struct. While that iterator is alive in a for loop, any attempt to call a mutating method (v.push(), v.remove(), v.clear()) requires &mut v and is rejected at compile time with E0502:
// ❌ does not compile
fn main() {
let mut v = vec![10, 20, 30];
for &item in v.iter() {
if item == 20 {
v.push(40); // Error: cannot borrow `v` as mutable while `v.iter()` is active
}
}
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> src/main.rs:7:13
|
5 | for &item in v.iter() {
| --------
| |
| immutable borrow occurs here
| immutable borrow later used here
6 | if item == 20 {
7 | v.push(40);
| ^^^^^^^^^^ mutable borrow occurs here
Safe Idioms for Modifying a Vector Based on Its Elements
Depending on what modification you want to perform, use one of three safe patterns instead of mutating v inside for x in &v:
1. In-Place Removal: Use retain()
When removing elements that match a condition, use v.retain(|x| ...):
fn main() {
let mut nums = vec![1, 2, 3, 4, 5, 6];
nums.retain(|&n| n % 2 != 0);
println!("Kept odds: {nums:?}");
}
Kept odds: [1, 3, 5]
2. Appending New Elements Derived from Existing Elements: Collect or Index
If you want to append new items derived from existing elements, either collect the new items into a temporary vector/extend from within, or iterate by index up to the initial length (0..initial_len):
fn main() {
let mut events = vec![10, 20, 30];
// Snapshot the initial length; indexing `events[i]` only borrows `events` for that expression:
let initial_len = events.len();
for i in 0..initial_len {
if events[i] == 20 {
events.push(200);
}
}
println!("Events after loop: {events:?}");
}
Events after loop: [10, 20, 30, 200]
3. Replacing Elements with Zero, One, or Many Items: Use flat_map or splice
When expanding elements into variable-length sequences, consume the vector with .into_iter().flat_map(...).collect().