Rust error: Cannot borrow as mutable because it is also borrowed as immutable

Can't change stuff when I am iterating over it

Fredrik Park published on
3 min, 415 words

Categories: Errors

As part of solving the advent of code challenges this year. I ran into a common issue regarding mutability and borrowing while iterating over a collection in Rust.

The problem

I hold a list of cards (Vec<Card>).

#[derive(Debug)]
struct Card {
    cards_held: usize,
    // More fields that are not interesting to this issue
}

I then need to go through all my cards and if I have a winning card I get a certain number of new cards (how many and why is not important).

The issue comes when I try to update my cards_held counter while going through my list of cards.

So let's look at a possible solution

fn part2(input: String) -> usize {
    let mut cards = parse(&input);

    for (index, card) in cards.iter().enumerate() {
        let num = winning_numbers_count(&card);
        let copies = card.cards_held;
        for i in 0..num {
            cards[index + i + 1].cards_held += 1 * copies;
        }
    }

    0 // Please the type signature while resolving the mutability issue.
}

And, here is the error I get when running that particular code.

error[E0502]: cannot borrow `cards` as mutable because it is also borrowed as immutable
  --> y2023/src/bin/day4.rs:91:13
   |
87 |     for (index, card) in cards.iter().enumerate() {
   |                          ------------------------
   |                          |
   |                          immutable borrow occurs here
   |                          immutable borrow later used here
...
91 |             cards[index + i + 1].cards_held += 1 * copies;
   |             ^^^^^ mutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `y2023` (bin "day4") due to previous error

So what it says is that iter() does an immutable borrow, so I am not allowed to do the mutable borrow I need in order to update the cards struct. Rust specifically, is keeping me from doing what I want.

The solution

The solution is quite simple. We need to break the connection between my list of cards and the cards_held count that I need to update.

So if we create a temporary cards_held to a separate structure. Do our counting using that structure, then we are able to update the value as needed using a mutable borrow.

fn part2(input: String) -> usize {
    let cards = parse(&input);
    let mut cards_held = Vec::with_capacity(cards.len());
    for card in cards.iter() {
        cards_held.push(card.cards_held);
    }

    for (index, card) in cards.iter().enumerate() {
        let num = winning_numbers_count(&card);
        let copies = cards_held[index];
        for i in 0..num {
            cards_held[index + i + 1] += 1 * copies;
        }
    }

    for (index, mut card) in cards.into_iter().enumerate() {
        card.cards_held = cards_held[index];
    }

    // Now we can use cards for whatever needs we have
    0 // Please the type signature for now
}