Skip to content
Select theme

Why can't I modify a Vec while iterating over it?

Short answer: Iterating over a Vec borrows it for the whole loop, and the methods that change it — push, remove, clear — need a mutable borrow of the same Vec. The compiler refuses, with E0502. What works instead: filter with retain, take the elements out with drain, or build a new Vec and swap it in.

From a fresh project — cargo new vec-mod-demo, then replace src/main.rs with each block below and run it with cargo run. The project name shows up only in cargo’s own output; nothing else here depends on it.

The usual shape is a loop that deletes or moves elements as it goes:

error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
--> src/main.rs:5:13
|
3 | for (i, n) in numbers.iter().enumerate() {
| - --------------------------
| | |
| | immutable borrow occurs here
| _____| immutable borrow later used here
| |
4 | | if n % 2 == 0 {
5 | | numbers.remove(i);
| | ^^^^^^^^^^^^^^^^^ mutable borrow occurs here
6 | | }
7 | | }
| |_____- this for loop borrows `numbers` immutably, preventing mutation within its body
|
= help: consider using an index-based loop instead, or collecting modifications into a separate collection
For more information about this error, try `rustc --explain E0502`.

iter() holds a shared borrow of numbers for the whole loop, and remove wants the Vec to itself. The two overlap, so the build fails before the program ever runs. The help line suggests an index-based loop instead; with remove that compiles, but it skips elements and then runs off the end — removal index (is 2) should be < len (is 2), exit code 101. The fixes worth taking are the ones below, and the full anatomy of E0502 is on the mutable-borrow page.

[1, 3, 5]

retain keeps the elements the closure returns true for and drops the rest, in one pass. The closure gets a reference to each element, the survivors keep their order, and the Vec is still there afterwards. Whenever the job is “delete the elements that match”, this is the call — the loop disappears entirely.

taken: ["Ada", "Grace", "Linus"]
names: []
len: 0, capacity: 3

drain(..) removes a range and hands the elements over as owned values — the Strings above move out, they are not copied — so the loop body finally owns what it iterates over. The Vec itself survives, empty: it still reports capacity 3, so refilling it to the same size costs nothing. Use it when the elements are leaving for good but the binding has to stay.

[2, 4, 6]
[1, 2, 3]

When each new value depends on the old ones — an average of neighbouring elements, say — the old Vec is the input and a new one is the output. iter().map(..).collect() builds the answer without touching the original; note that nothing here is mut, because nothing is being modified. If the original isn’t needed afterwards, into_iter() in place of iter() consumes it and moves the elements out instead of borrowing them: over the list above, into_iter().filter(|n| n % 2 == 1).collect() gives [1, 3].

One more case, since “modify” covers it: when the loop only writes through the element it is holding, iter_mut() hands out one &mut per iteration and there is nothing to conflict with. That loop is on the mutable-borrow page too.

Next in this chapter: what is the difference between a Vec and a slice?