Rust error: Cannot borrow as mutable because it is also borrowed as immutable
Can't change stuff when I am iterating over it
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
}