1

I'm making a tile-based game with structures that span multiple tiles. Every tile under the structure has to have a mutable reference to that structure. For a structure that spans two tiles, the 2D array that represents the map has to simultaneously contain two mutable references to the structure.

I also have a list that mutably owns all my structures (for iteration)

I tried this with &mut and failed:

let mut all_structures: Vec<Box<Structure>> = Vec::new();
let mut grid: [[Vec<&mut Box<Structure>>; 4]; 4] = Default::default(); // accessed [y][x]

let mut house: Box<Structure> = Box::new(House { });
grid[1][1].push(&mut house);
grid[1][2].push(&mut house);
all_structures.push(house);

with error:

error[E0499]: cannot borrow `house` as mutable more than once at a time
  --> src/main.rs:21:21
   |
20 |     grid[1][1].push(&mut house);
   |                     ---------- first mutable borrow occurs here
21 |     grid[1][2].push(&mut house);
   |                ---- ^^^^^^^^^^ second mutable borrow occurs here
   |                |
   |                first borrow later used by call

Right now this is just single threaded so nothing needs to be thread-safe

I put the code on the playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Nate
  • 481
  • 3
  • 11
  • 1
    "Right now this is just single threaded so nothing needs to be thread-safe" → you can have unsafe programs with multiple mutable references to the same object without needing threads (classical example: you iterate on a vector and try to push some element to it in your loop, common UB in C++), so Rust forbids it. – mcarton Jan 10 '19 at 23:30
  • Yeah but there's got to be some way to do this otherwise a lot of applications become very difficult. Thats why rust has wrapper types in the first place. – Nate Jan 10 '19 at 23:44
  • Related: https://stackoverflow.com/questions/51222712/why-is-it-possible-to-have-multiple-mutable-references-with-static-lifetime-in-s – hellow Jan 11 '19 at 07:06
  • Also related: https://users.rust-lang.org/t/why-cant-we-have-multiple-mut/23853 – hellow Jan 11 '19 at 07:09

1 Answers1

3

The simplest option would be to use Rc<RefCell<Structure>> instead of Box<Structure>.

Rc<T> supports shared ownership of a value of type T, which lets you have multiple pointers to the same object with automatic lifetime management. Rc<T> is not thread-safe; you can change to using Arc instead if you later find you need thread-safety.

RefCell<T> allows you to enforce the Rust borrowing rules (that is, that there can be either one &mut borrow or any number of & borrows) at runtime rather than at compile time: this means it's OK to have multiple paths by which you could potentially mutate the same object, so long as you don't actually do so.

I've put a complete example up on the playground. Note that this uses .borrow() and .borrow_mut(), which will panic if you actually end up violating the borrowing rules (e.g., by mutably borrowing the same Structure multiple times at once).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Richard Smith
  • 13,696
  • 56
  • 78