I want to implement two different structures that have references to each other, can mutate each other, and are thread-safety.
I can introduce the following example:
pub struct Player {
pub x: f32,
pub y: f32,
game: Option<Game>, // Reference to `Game`
}
impl Player {
pub fn new(x: f32, y: f32) -> Self {
Player { x, y, game: None}
}
// Some functions that can change `self`, `game`, and `map`
}
pub struct Game {
pub map: GameMap,
players: Vec<Player>, // Reference to `Player`
}
impl Game {
pub fn new() -> Self {
Game { map: GameMap::new(), players: Vec::new()}
}
pub fn register_player(&mut self, player: Player) {
todo!();
}
}
I'd like to have a similar interface:
fn main() {
let mut p1 = Player::new(0.0, 0.0);
let mut p2 = Player::new(100.0, 100.0);
let mut game = Game::new();
game.register_player(p1);
game.register_player(p2);
p1.forward(); // changes its coordinates using `map` from `game`
p2.shoot(); // changes the `map` and possibly another player
}
I can't use std::rc::Rc and std::cell::RefCell because my goal is to write thread-safety code. I tried std::sync::{Arc, Mutex}, but it's not succeeded yet. How could I solve this challenge?
UPD Here I add the code where I tried to use mutex
use std::sync::{Arc, Mutex};
pub struct Player {
pub x: f32,
pub y: f32,
game: Option<Arc<Mutex<Game>>>,
}
impl Player {
pub fn create(x: f32, y: f32) -> Arc<Mutex<Self>> {
let mut player = Player {
x,
y,
game: None,
};
Arc::new(Mutex::new(player))
}
pub fn mount_game(&mut self, game: Arc<Mutex<Game>>) {
self.game = Some(game);
}
}
pub struct Game {
players: Vec<Arc<Mutex<Player>>>,
}
impl Game {
pub fn create() -> Arc<Mutex<Self>> {
let mut game = Game {
players: Vec::new(),
};
Arc::new(Mutex::new(game))
}
pub fn register_player(&self, game_arc: Arc<Mutex<Self>>, player_arc: Arc<Mutex<Player>>) {
let mut game = game_arc.lock().unwrap();
game.players.push(Arc::clone(&player_arc));
player_arc.lock().unwrap().mount_game(Arc::clone(&game_arc));
}
}
fn main() {
let mut p1 = Player::create(0.0, 0.0);
let mut p2 = Player::create(0.0, 0.0);
let mut game = Game::create();
game.lock().unwrap().register_player(Arc::clone(&game), Arc::clone(&p1));
game.lock().unwrap().register_player(Arc::clone(&game), Arc::clone(&p2));
}
The code you posted deadlocks because you lock the game mutex twice:
mainwhen you callgame.lock(),register_playerwhen you callgame_arc.lock().Moreover from an API standpoint, needing to pass the game twice to
register_player(once asselfand once asgame_arc) is pretty awkward.Now if we get rid of the mutex around
Gameso that we have anArc<Game>, then we can declareregister_playeras:Playground
But then we need to put a
MutexinsideGameif we want to be able to mutate it:In this case
Gamecontains a single field so it's easy, but assuming your game state is more complex, this becomes painful to use.It would be nice if we could tell the compiler that
register_playertakes an&Arc<Mutex<Self>>, but unfortunately we can only use types thatDereftoSelfandMutexcan't (*). We also can't add theregister_playermethod directly onMutex<Game>like this:because
Mutexis a foreign type. We can however fake it using an extension trait:Finally, having an
Arc<Player>insideGameand anArc<Game>insidePlayeris a bad idea because it risks introducing memory leaks: they will still refer to each other even when you drop all other references, so their respective reference counts will never reach 0 and they will never be freed. This can be avoided by replacing one of theArcs by aWeakpointer, which will break the cycle.Full working example (I also removed a bunch of superfluous
muts sinceArc<Mutex<_>>values don't need to bemutbefore they're locked):Playground
(*)
Mutex<Foo>can'tDereftoFoobecause it needs somewhere to put theMutexGuardwhile the reference is live, andDerefdoesn't offer anywhere to put the guard.