---
title: "18.3 Vec: iterating and borrowing"
description: "Iterating a vector by shared reference, mutable reference, and by value, and why the borrow checker forbids modifying a vector while iterating over it."
url: "https://learnrust.net/chapter-18/vec-iterating-and-borrowing/"
last_updated: "2026-06-13"
---

# 18.3 Vec: iterating and borrowing

Iterating a `Vec` is something you'll do constantly, and it's where the borrow checker from chapter 9 earns its keep in everyday code. There are three ways to loop over a vector, matching the three ways to access any value, and there's one classic bug, modifying a collection while looping over it, that Rust turns into a compile error. This lesson covers both.

## The three ways to iterate

A `for` loop (lesson [7.5](https://learnrust.net/chapter-7/for-loops-and-ranges/)) over a vector can borrow each element immutably, borrow it mutably, or take it by value, the same `&` / `&mut` / owned choice as any function parameter (lesson [9.4](https://learnrust.net/chapter-9/references-and-functions/)).

**`for x in &vec`** borrows each element immutably, for reading:

```rust
fn main() {
    let numbers = vec![1, 2, 3];
    for n in &numbers {
        println!("{n}");
    }
    println!("still here: {numbers:?}");   // numbers still usable
}
```

```
1
2
3
still here: [1, 2, 3]
```

`&numbers` borrows the vector, so the loop reads each element without consuming anything, and `numbers` is still usable afterward. This is the most common form.

**`for x in &mut vec`** borrows each element mutably, to change them in place:

```rust
fn main() {
    let mut numbers = vec![1, 2, 3];
    for n in &mut numbers {
        *n *= 10;
    }
    println!("{numbers:?}");
}
```

```
[10, 20, 30]
```

`&mut numbers` gives a mutable borrow of each element, and `*n` dereferences to modify it (the `*` from lesson [9.6](https://learnrust.net/chapter-9/slices/)). The vector must be `mut`. After the loop, `numbers` holds the changed values.

**`for x in vec`** (no `&`) takes each element by value, *consuming* the vector:

```rust
fn main() {
    let words = vec![String::from("a"), String::from("b")];
    for w in words {
        println!("{w}");        // w owns each String
    }
    // println!("{words:?}");   // refused: words was moved
}
```

```
a
b
```

Without the `&`, the loop *moves* the vector's contents out, so `words` is consumed and can't be used afterward (lesson [8.4](https://learnrust.net/chapter-8/moves/)). Use this when you want to take ownership of the elements (to move each into something else); use `&vec` when you only need to read, which is more common.

> **Best practice**
>
> Default to `for x in &vec` (borrow to read). Reach for `for x in &mut vec` when you need to modify elements in place, and `for x in vec` only when you genuinely want to consume the vector and take ownership of its elements. Accidentally writing `for x in vec` when you meant `&vec` is a common early mistake; the giveaway is a later "value moved" error on the vector. The `&` is rarely what you want to omit.

## You can't modify a vector while iterating it

Here's the bug Rust prevents. A classic mistake in many languages is to add to or remove from a collection while looping over it, which can invalidate the loop's position and cause crashes or skipped elements. Rust makes it a compile error via the borrow checker (chapter 9):

```rust
fn main() {
    let mut numbers = vec![1, 2, 3];
    for n in &numbers {
        if *n == 2 {
            numbers.push(4);     // modify while iterating: refused
        }
    }
}
```

```
error[E0502]: cannot borrow `numbers` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:13
  |
3 |     for n in &numbers {
  |              --------
  |              |
  |              immutable borrow occurs here
  |              immutable borrow later used here
5 |             numbers.push(4);
  |             ^^^^^^^^^^^^^^^ mutable borrow occurs here
```

This is E0502 from lesson [9.3](https://learnrust.net/chapter-9/the-borrow-checker-is-your-friend/), the exact same "can't have a writer while readers live" rule, here catching a real iteration bug. The `for n in &numbers` holds an immutable borrow of the vector for the whole loop, and `numbers.push(4)` needs a mutable borrow, which would have to coexist with the immutable one. The borrow checker refuses, and in doing so it prevents the genuine hazard: `push` might force the vector to reallocate its heap buffer (lesson [18.4](https://learnrust.net/chapter-18/vec-capacity-and-growth/)), which would leave the loop's reference dangling. The rule you learned abstractly in chapter 9 is here protecting you from a concrete, notorious bug.

> **Key insight**
>
> "Don't mutate a collection while iterating it" is a rule other languages state in documentation and hope you follow; in Rust the borrow checker enforces it, because iterating borrows the collection and mutating needs a conflicting borrow. The same E0502 that felt abstract in chapter 9 turns out to guard one of the most common real-world collection bugs. When you hit it, the fix is usually to collect the changes you want and apply them after the loop, or to iterate over indices, or to use a method like `retain` that's designed for filtering in place.

## Working around it

When you genuinely need to modify based on iteration, common patterns: collect the indices or values to change into a separate `Vec` during the loop, then apply the changes after; or use a purpose-built method like `retain(|x| ...)` which removes elements matching a condition safely in one call; or iterate over a range of indices (`for i in 0..vec.len()`) and index into the vector, since that doesn't hold a borrow across the whole loop. The borrow error is the compiler steering you toward one of these correct approaches instead of the broken one.

## Quiz time

**Question #1**

What's the difference between `for x in &vec`, `for x in &mut vec`, and `for x in vec`?

<details class="solution">
<summary>Show solution</summary>

`for x in &vec` borrows each element immutably (read-only; the vector survives the loop). `for x in &mut vec` borrows each mutably (modify in place; vector must be `mut`, survives the loop). `for x in vec` takes each element by value, *consuming* the vector (it can't be used afterward). It's the same `&` / `&mut` / owned choice as function parameters (lesson [9.4](https://learnrust.net/chapter-9/references-and-functions/)).

</details>

**Question #2**

Why does the compiler refuse `numbers.push(4)` inside `for n in &numbers`?

<details class="solution">
<summary>Show solution</summary>

Because `for n in &numbers` holds an immutable borrow of the vector for the whole loop, and `push` needs a mutable borrow, which can't coexist with the immutable one (E0502, lesson [9.3](https://learnrust.net/chapter-9/the-borrow-checker-is-your-friend/)). This prevents a real bug: `push` may reallocate the vector's buffer, which would invalidate the loop's reference. It's the chapter-9 rule catching a classic mutate-while-iterating mistake.

</details>

**Question #3**

You want to remove all even numbers from a `Vec<i32>`. Why can't you do it with a `for` loop that calls `remove`, and what's a clean alternative?

<details class="solution">
<summary>Show solution</summary>

A `for` loop over the vector borrows it, so calling `remove` (which needs a mutable borrow) inside is refused by the borrow checker, and mutating during iteration would also skip or misindex elements. The clean alternative is `numbers.retain(|n| n % 2 != 0)`, a method built to filter in place safely in one call. (Other options: build a new filtered `Vec`, or iterate indices carefully.)

</details>

A vector grows as you push, but *how*? The next lesson looks under the hood at capacity versus length, why growth occasionally reallocates, and how `with_capacity` avoids repeated reallocation when you know the size ahead of time.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
