I am learning Rust by exercise. In this file the goal is to update cells as in a spreadsheet: when a value changes, all cells that are derived from it have to be recalculated. Here, those are called that cell's parents.
Updating a cell value proves no problem, but updating the parents gets me fighting the borrow checker. When I have retrieved the cell from a HashMap
and updated the value, I no longer need a mutable reference - so I tried wrapping it in an immutable reference instead. That way I only have to find it once.
But it seems Rust figures since I originally got my immutable reference from a borrowed &mut self
, it must still be tied to it. This obviously prevents me from reusing self
a second time.
use std::collections::HashMap;
use std::vec::Vec;
struct Cell {
value: i32,
parents: Vec<u32>,
}
pub struct Sheet {
table: HashMap<u32, Cell>,
}
impl Sheet {
pub fn set_value(&mut self, identifier: u32, new_value: i32) {
let mut updated_cell: Option<&Cell> = None;
if let Some(cell) = self.table.get_mut(&identifier) {
let Cell { value, .. } = cell;
*value = new_value;
updated_cell = Some(cell);
}
if let Some(cell) = updated_cell {
recalculate(self, &cell.parents);
}
}
}
fn recalculate(_sheet: &mut Sheet, _cells: &[u32]) {}
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:20:16
|
16 | if let Some(cell) = self.table.get_mut(&identifier) {
| ---------- first mutable borrow occurs here
...
22 | recalculate(self, &cell.parents);
| ^^^^ ------------- first borrow later used here
| |
| second mutable borrow occurs here
I want to know if there is a solution which avoids searching a second time or taking an unnecessary vector copy. I have tried to adjust the code many times, but not all of the syntax is yet clear to me yet.
Rust is protecting you from a potentially dangerous situation. There is nothing in recalculate
's signature to guarantee that it won't mutate sheet
in such at way that the references in cells
become invalid. For example recalculate
could delete some cells and then the references in cell.parents
would be dangling pointers.
You probably need to pass a clone of the parent cells instead:
if let Some(cell) = updated_cell {
let parents = cell.parents.clone();
recalculate(self, &parents);
}
Alternatively, you may need to consider a different data model, which separates the mutability of individual cells from the mutability of the overall structure. For example, you could wrap cells in a std::cell::Cell
or std::cell::RefCell
, and pass an immutable reference to the Sheet
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With