How to work with vectors of mutables in rust?

I am trying to teach myself rust by building a poker game in it. While doing that I have stumbled over the mutability of fields. I want to make a list of players in my main function. These players are given to a game object, that lists them as participants (implying a need to create a new, mutable copy of references to the players, so that when a player folds, he can be removed from the participants of the game). I have provided an excerpt of my implementation below. If I do it like this, I can't change the amount of money a player has, and I don't understand why, because both the game and the players are mutable.

struct Player {
    money: i32
}

struct Game <'a> {
    participants: Vec<&'a Player>,
    cur_player: &'a Player
}

fn start_game<'a>(players: &'a Vec<Player>) -> Game<'a> {
    let participants: Vec<&Player> = players.iter().collect();
    Game {
        cur_player: participants[0],
        participants: participants,
    }
}

fn main() {
    let mut players = Vec::new();
    for _ in 0..4 {
        let mut player = Player { money: 100 };
        players.push(player);
    }
    let mut game = start_game(&players);
    game.participants[0].money = 90;
}

Mutation in Rust requires unique access to the thing being mutated; your code shares the players via shared reference &'a Player. Mutating through these is possible, but only when using some "interior mutability" primitives like Cell or RefCell. (Or Mutex if you need thread-safety; I'm assuming you don't use multi-threading in your poker game.)

So one possible straightforward approach is to just wrap the field of Player (which are of simply Copy-able types) that need to be mutated into Cells. Alternatively you could also e.g. wrap the whole Player in a RefCell. Just to name two possible approaches.

Using a Cell<i32> for money:

use std::cell::Cell;

struct Player {
    money: Cell<i32>,
}

struct Game<'a> {
    participants: Vec<&'a Player>,
    cur_player: &'a Player,
}

fn start_game<'a>(players: &'a Vec<Player>) -> Game<'a> {
    let participants: Vec<&Player> = players.iter().collect();
    Game {
        cur_player: participants[0],
        participants: participants,
    }
}

fn main() {
    let mut players = Vec::new();
    for _ in 0..4 {
        let player = Player { money: 100.into() };
        players.push(player);
    }
    let game = start_game(&players);
    game.participants[0].money.set(90);
}

Player in a RefCell

use std::cell::RefCell;

struct Player {
    money: i32,
}

struct Game<'a> {
    participants: Vec<&'a RefCell<Player>>,
    cur_player: &'a RefCell<Player>,
}

fn start_game<'a>(players: &'a Vec<RefCell<Player>>) -> Game<'a> {
    let participants: Vec<&RefCell<Player>> = players.iter().collect();
    Game {
        cur_player: participants[0],
        participants: participants,
    }
}

fn main() {
    let mut players = Vec::new();
    for _ in 0..4 {
        let player = RefCell::new(Player { money: 100 });
        players.push(player);
    }
    let game = start_game(&players);
    game.participants[0].borrow_mut().money = 90;
}

Instead of interior mutability, there's also the possibility to avoid sharing the Player at all. For example, cur_player could just be an index into the participants vector, which could simply contain owned instances of Player:

struct Player {
    money: i32,
}

struct Game {
    participants: Vec<Player>,
    cur_player_ix: usize,
}

fn start_game(players: Vec<Player>) -> Game {
    Game {
        cur_player_ix: 0,
        participants: players,
    }
}

fn main() {
    let mut players = Vec::new();
    for _ in 0..4 {
        let player = Player { money: 100 };
        players.push(player);
    }
    let mut game = start_game(players);
    game.participants[0].money = 90;
}

If you can re-work your code into something like this last approach, it would become particularly straight-forward to work with and reason about, no need to bother with interior mutability; also the lifetime arguments on the Game struct are gone (eliminating the lifetimes would otherwise, with shared Players, have required using something like Rc).


Edit: Re-reading your original post, I noticed

so there's apparently a list of all players and a different list of players that are still actively participating in the current round. In this case, the owned Vec<Player> for participants probably wouldn't work. You could instead use a Vec<&'a mut Player> (which might work well, provided that the no-longer participating players don't really need to be handled at all in your game, before the next round). Or you could even use a Vec<usize> with indices into yet-another Vec of players so you can e.g. pass to the Game object a short-lived mutable reference to that global Vec<Player> for every method that needs it.

Though maybe then, after all, using interior mutability might be the more straightforward approach for you, since it requires less careful design of your data structures around ownership and borrowing. If lifetimes do become a problem, then keep in mind that Rc<RefCell<Player>> (instead of &'a RefCell<Player>) or Rc<Player> (instead of &'a Player, when you use field with interior mutability like the Cell<i32>) is an option, too.

Wow, thank you so much for the elaborate reply! I think that cell/refcell would be the right way to go, if I want to do it the way I intended to. I had missed the core concept of unique access for mutation. I had read through the Variables and Mutability section, but it didn't mention the requirement. Do you know where I can learn more about the idea of cells? I know you gave me a link to the API documentation of cells, but I still feel like I lack the core principles behind it and the need for it.

The chapter 4 on ownership and borrowing explains some stuff. In particular 4.2. References and Borrowing

We call the action of creating a reference borrowing . As in real life, if a person owns something, you can borrow it from them. When you’re done, you have to give it back.

So what happens if we try to modify something we’re borrowing? Try the code in Listing 4-6. Spoiler alert: it doesn’t work!

Just as variables are immutable by default, so are references. We’re not allowed to modify something we have a reference to.

But mutable references have one big restriction: you can have only one mutable reference to a particular piece of data at a time.

The restriction preventing multiple mutable references to the same data at the same time allows for mutation but in a very controlled fashion. It’s something that new Rustaceans struggle with, because most languages let you mutate whenever you’d like.

The benefit of having this restriction is that Rust can prevent data races at compile time.

We also cannot have a mutable reference while we have an immutable one. Users of an immutable reference don’t expect the values to suddenly change out from under them! However, multiple immutable references are okay because no one who is just reading the data has the ability to affect anyone else’s reading of the data.


On RefCell (and also mentioning Cell and Mutex in the end), you can read more in chapter 15: 15.5. RefCell<T> and the Interior Mutability Pattern

Cell is somewhat more lightweight than RefCell; it doesn't need any runtime-checks for preventing exclusive mutable use, but in turn it places further restrictions on what you can do with it: you cannot obtain any references to the contained data; you need to copy or swap the value out. For copyable types such as i32, that's not too bad, so Cell<i32> would generally be preferred over RefCell<i32>. For more on Copy, the chapter 4 on ownership contains some information.

Super valuable, thank you so much!