I think the architecture of my project might be past saving. I want to ask "how do I solve the error on line 287?" but I know full well that I'm probably sitting on top of a jenga tower of mutable refs and over-borrowed structs.
To share a recent hack for dodging the borrow checker, I have an object you call methods on to mutate its internal state. It wants to update a couple of different cells in a 2d grid, but before that it has to check some other cells in the grid. If everything looks good, then it does some record keeping on the board and then returns an event to the caller, signaling where the ship is going to move.
pub fn naive_navigate(&mut self, ship: Ship, destination: &Position) -> Direction {
let ship_position = &ship.position;
let current_cell = &self.occupied[ship.position.y as usize][ship.position.x as usize];
let is_yard = match current_cell.structure {
Some(_) => true,
None => false,
};
if ship.halite > 0 && is_yard {
for direction in self.get_unsafe_moves(&ship_position, destination) {
let target_pos = ship_position.directional_offset(direction);
if self.is_safe(&target_pos) {
self.mark_unsafe(&target_pos, ship.clone());
self.mark_unsafe_friendly(&target_pos, ship.clone());
return direction;
}
}
}
Direction::Still
}
This yields an error, however:
error[E0502]: cannot borrow `*self` as mutable because `self.occupied` is also borrowed as immutable
--> src/hlt/navi.rs:297:21
|
287 | let current_cell = &self.occupied[ship.position.y as usize][ship.position.x as usize];
| ------------- immutable borrow occurs here
...
297 | self.mark_unsafe(&target_pos, ship.clone());
| ^^^^ mutable borrow occurs here
...
305 | }
| - immutable borrow ends here
error[E0502]: cannot borrow `*self` as mutable because `self.occupied` is also borrowed as immutable
--> src/hlt/navi.rs:298:21
|
287 | let current_cell = &self.occupied[ship.position.y as usize][ship.position.x as usize];
| ------------- immutable borrow occurs here
...
298 | self.mark_unsafe_friendly(&target_pos, ship.clone());
| ^^^^ mutable borrow occurs here
...
305 | }
| - immutable borrow ends here
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0502`.
On a wild hunch, I inline some stuff into a single expression, so my state-check:
let current_cell = &self.occupied[ship.position.y as usize][ship.position.x as usize];
let is_yard = match current_cell.structure {
Some(_) => true,
None => false,
};
Becomes a single let
:
let is_yard =
match &self.occupied[ship.position.y as usize][ship.position.x as usize].structure {
Some(_) => true,
None => false,
};
And now the compiler error is gone and my code runs.
Some problems that I need help fixing:
- This is an unreadable hack.
- What can I do to actually learn the borrow checker instead of work on messy projects and learn to layer terrible fixes to run away from it?
- How can I learn more Rust-appropriate architectures so that it feels like I'm working with Rust instead of wholly against it?
It seems like there's a ray of hope in learning more about Entity Component Systems, but so far the premise of them seems like "what if instead of domain objects, you modeled everything as vectors of integers that hate each other?" Maybe the corresponding post is supposed to be more practical, but I've got some kind of a paradigm blindspot when you switch to an environment that's totally different but you keep trying to solve problems in the way you're familiar with.
Would love some guidance or advice for how I'm supposed to reframe this so Rust is fun again.
Thanks!