1

I am implementing the board game Nine Man's Morris in Rust. I have a Game struct which owns a Board struct. Board stores a RefCell<HashSet> of references to a Position struct. Board and Game share lifetime parameters.

pub struct Board<'a> {
    pub positions: Vec<Position>,
    pub ids_to_positions: HashMap<String, usize>,
    p1_mills: RefCell<HashSet<(&'a Position, &'a Position, &'a Position)>>,
    p2_mills: RefCell<HashSet<(&'a Position, &'a Position, &'a Position)>>,
    can_mill: Cell<bool>,
}
pub struct Game<'a> {
    pub board: Board<'a>,
    pub player1: Player,
    pub player2: Player,
    current_player_id: i8,
}

Game::game_loop loops through a set of methods (get input, update the board, etc) until the game has ended. This has been working fine as I've added methods to it.

impl<'a> Game<'a> {
    pub fn print(&self) {
        self.board.print();
    }

    pub fn game_loop(&'a mut self) {
        loop {
            self.print();
            self.make_move();
            print!("can_mill: {}", self.board.can_mill());
            self.board.update_mills(self.current_player_id);
            self.switch_player();
        }
    }
    pub fn make_move(&mut self) {}
    pub fn switch_player(&self) {}
}

There is a mix of methods with mutable and immutable references to self, and 2 calls on Board:

pub fn can_mill(&self) -> bool {}
pub fn update_mills(&'a self, player_id: i8) {}

update_mill updates the p1_mills and p2_mills fields, and can_mill references the can_mill field.

If I remove the update_mills call in game_loop, the code compiles. With it, I get

cannot borrow *self as mutable because self.board is also borrowed as immutable.

I'm pretty sure this has something to with the explicit lifetime in that method, but in all my reading around I haven't been able to get it to work or understand what isn't working. It's confusing because without I don't get any borrowing errors. I'm also not clear if having the mill sets in RefCells is breaking anything (I can't actually remember why there are in them in the first place to be honest).

I realise this is quite involved but I'd really appreciate some help. I tried to recreate the issue with a simpler example but couldn't sadly.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
UsAndRufus
  • 385
  • 5
  • 14
  • Please provide a [MCVE]. If you can reproduce the issue in the [Rust playground](//play.rust-lang.org), that would be great. – E_net4 Jan 04 '18 at 18:25
  • Probably a duplicate of [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423) – Shepmaster Jan 04 '18 at 18:26
  • Thanks @Shepmaster, it seems that was the issue in the end, although the compiler message didn't seem to be saying so! – UsAndRufus Jan 04 '18 at 20:39

1 Answers1

0

It turns out that my problem is, indeed, an abstracted version of Why can't I store a value and a reference to that value in the same struct? I think using the RefCells sort of got around the issue, but actually really just abstracted the issue away from me.

Got rid of the RefCells, removed the lifetime parameters, and made a little Mill struct that stored usizes to index the Position Vec with.

UsAndRufus
  • 385
  • 5
  • 14
  • Some advice for other newcomers like me would be - don't use smart pointer stuff like `RefCell` unless you _really_ know what you are doing with them and properly get lifetime stuff. – UsAndRufus Jan 04 '18 at 21:07