Problems with RefCell and for loops

I have the following function signature:
pub fn start_hand<'a>(&mut self, state: &'a State<'a>) {
The State struct holds game state information which I need to pass around. Because various functions will need to interact mutably and immutably with its members, each member of State is wrapped in a RefCell. But I am now having difficulties because the borrow checker is not allowing things like:
for player in state.players.borrow().iter() {
The compiler signals to me that a temporary is being dropped at the end of this statement while a borrow is still occurring. So I tried creating a scope for the for loop and assigning this iterator to a variable:
{
let mut players = state.players.borrow().iter();
for player in players {
state.table.borrow_mut()[0].add(player, 0);
}
}
No dice. This time, I am told that players does not live long enough. My main confusion here is that I see no reason why players should violate the 'a lifetime on the function. Ultimately I am referencing a member of the state which itself has a lifetime of 'a, so players should also live as long as 'a. And simply creating this let binding within the main function body and letting it be dropped at the end of the function is undesirable for the obvious reason that as soon as this needs to be pass mutably to another function, I'll get a panic even if it does compile. At this point, I am unsure how to continue.

This is not entirely clear from the information you provided, but I'm assuming that you have a type like so:

struct<'a> State<'a> {
    players: RefCell<Vec<Player>>,
    table: RefCell<HashMap<usize, &'a Player>>,
}

You're trying to create a self-referential type: State contains both players and references to elements in players. This is not possible in Rust. You should re-architect your code. For example, you can instead use reference-counting to share ownership of the Player, or you can use an Entity Component System like specs.

1 Like

This is what the State struct looks like. The method I referred to exiss in the Deck struct, an instance of which is held in the State struct:
pub struct State<'a> {
pub voice: RefCell,
pub players: RefCell<Vec>,
pub board: RefCell<Vec>,
pub deck: RefCell,
pub table: RefCell<Table<'a>>,
pub blinds: RefCell,
pub between_hands: RefCell,
pub between_hands_interval: RefCell,
}
You're suggestion, then, is to replace the RefCells with RCs?

So I have gone through and wrapped everything in State in Rc<RefCel>s. This has, apparently broken my indexing, however.
A Table consists of a series of pots in a Vector, and I have the Index traits overloaded to return individual pots when tables are indexed. Likewise, each Pot consists of a series of contributions ot that pot, which are indexed in the same way. I had to wrap the Vec of contributions in Rc and RefCell also, and the Index traits now throw errors because I am referencing a temporary value. It seems to me, though, that as soon as you have to call borrow on a RefCell, you will always end up with a temporary value. Does the Rc RefCell pattern basically render indexing inoperable? I have another interface I can use to gain access to the fields I need, but syntactic sugar is always nice when I can use it.
Just for reference, here is the relevant code:
struct Pot {
size: i32,
contributions: Rc<RefCell<Vec>>,
}

impl Index for Pot {
type Output = Contribution;
fn index(&self, i: usize) -> &Self::Output {
&self.contributions.borrow()[i]
}
}

impl IndexMut for Pot {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
&mut self.contributions.borrow()[i]
}

Hum, it is currently quite hard to help you, given that your code has been "stripped" by the forum platform :grimacing:

  • Please use ``` around your code to get syntax higlighting, and more importantly, prevent the forum platform from stripping code that happens to be within angle brackets < ... >:

    ```rust
    // your code here
    ```
    
  • Lastly, try to get a minimal reproducible example of your problem, so that you can copy-paste it entirely within a single post :slightly_smiling_face:. For instance, given that start_hand's issue only seems to happen when dealing with both the .players and .table fields, you can start by having a struct with just those two fields., and having that start_hands function (no need to have constructors or anything else, just the fields and methods involved / that appear in the body of the problematic function.