I'm currently experimenting on a game in Rust and am having an issue working out the best way to handle the tiles. My end goal is that all tiles in a world will have an update method, and are able to modify and access other tiles in the same world, ideally as well as other properties of the "world", all tiles should be able to create, remove and update other tiles.
I've put together some sample code quickly which should hopefully show how I'd like it to work, is there a way that I can achieve what I'm looking for? Apologies for the mistakes in the code, I threw it together quickly.
// Tile needs to be able to modify itself during an update tick, as well as
// other tiles stored in the same world.
use std::rc::Rc;
use std::cell::*;
use std::collections::HashMap;
struct Tile {
counter: i32,
pos: (i32, i32)
}
impl Tile {
pub fn new(x: i32, y: i32) -> Tile {
Tile {
counter: 0,
pos: (x, y)
}
}
pub fn update(&mut self, world: RefCell<World>) {
let mut w = world.borrow_mut();
if w.get_tile_at(self.pos.0 + 1, self.pos.1).is_some() {
w.destroy_tile(self.pos.0 + 1, self.pos.1);
self.counter += 1;
}
}
}
struct World {
tiles: HashMap<(i32, i32), Rc<RefCell<Tile>>>
}
impl World {
pub fn new() -> World {
World {
tiles: HashMap::new()
}
}
pub fn set_tile_at(&mut self, x: i32, y: i32, tile: Tile) {
self.tiles.insert((x, y), Rc::new(RefCell::new(tile)));
}
pub fn get_tile_at(&self, x: i32, y: i32) -> Option<RefMut<Tile>> {
match self.tiles.get(&(x, y)) {
Some(t_ref) => Some(t_ref.borrow_mut()),
_ => None
}
}
pub fn destroy_tile(&mut self, x: i32, y: i32) {
self.tiles.remove(&(x, y));
}
}
struct App {
world: RefCell<World>
}
impl App {
pub fn new() -> App {
App {
world: RefCell::new(World::new())
}
}
pub fn update(&mut self) {
let mut tile = self.world.borrow().get_tile_at(0, 0).unwrap();
tile.update(self.world);
}
}
fn main() {
let mut app = App::new();
{
let mut world = app.world.borrow_mut();
world.set_tile_at(0, 0, Tile::new(0, 0));
world.set_tile_at(1, 0, Tile::new(1, 0));
}
app.update();
{
let world = app.world.borrow();
let t1 = world.get_tile_at(1, 0);
assert!(t1.is_none());
let t0 = world.get_tile_at(0, 0);
assert!(t0.is_some());
assert!(t0.unwrap().counter == 1);
}
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:67:24
|
67 | let mut tile = self.world.borrow().get_tile_at(0, 0).unwrap();
| ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
68 | tile.update(self.world);
| ---- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0507]: cannot move out of borrowed content
--> src/main.rs:68:21
|
68 | tile.update(self.world);
| ^^^^^^^^^^ cannot move out of borrowed content
error[E0505]: cannot move out of `self.world` because it is borrowed
--> src/main.rs:68:21
|
67 | let mut tile = self.world.borrow().get_tile_at(0, 0).unwrap();
| ---------- borrow of `self.world` occurs here
68 | tile.update(self.world);
| ^^^^^^^^^^ move out of `self.world` occurs here
69 | }
| - borrow might be used here, when `tile` is dropped and runs the destructor for type `std::cell::RefMut<'_, Tile>`
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0505, E0507, E0716.
For more information about an error, try `rustc --explain E0505`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.