In this lesson, you will learn how to bypass Rust's strict ownership rules to modify fields of structs while iterating through a collection using interior mutability.
The Borrowing Challenge
Normally, Rust’s ownership rules prevent you from having a mutable reference to an item if you are already iterating over the collection (the "aliasing XOR mutability" rule). If you have a Vec<T>, iterating over it usually grants you immutable access. If you try to change a field of one of those items, the borrow checker will stop you because it fears you might invalidate the collection's structure or create data races.
Enter Interior Mutability
To solve this, we use the Cell or RefCell types. These provide "interior mutability," a pattern that allows you to mutate data even when you only have an immutable reference (&T) to the container. By wrapping a struct field in a Cell, you move the borrow checking from compile-time to runtime.
Consider a system where you need to track the "hit count" of items inside a list:
use std::cell::Cell;
struct Item {
id: u32,
hit_count: Cell<u32>,
}
fn main() {
let items = vec![
Item { id: 1, hit_count: Cell::new(0) },
Item { id: 2, hit_count: Cell::new(0) },
];
// Even though 'items' is not declared as 'mut',
// we can still update the hit_count.
for item in &items {
let current = item.hit_count.get();
item.hit_count.set(current + 1);
}
}
Why This Works
Because Cell uses get and set methods rather than direct references, it doesn't give you a &mut to the internal data that could be held simultaneously by others. It performs a simple copy or swap of the value. This allows you to perform in-place updates while traversing a collection that you don't (or cannot) own as mutable. This is particularly useful when you need to update internal state (like counters, flags, or cache hits) without needing to re-borrow the entire parent collection as mutable.
Key Takeaway: Use interior mutability wrappers like Cell to update specific fields of structs within immutable iterations, effectively shifting the burden of borrow enforcement from the compiler to your own logic.