Question on holding related data in a struct.
from modulus@lemmy.ml to rust@programming.dev on 25 Mar 2024 11:14
https://lemmy.ml/post/13621174

I have a struct that looks like this:

pub struct Game {
    /// A HashSet with the players waiting to play as account strings.
    lobby: HashSet<String>,
    /// capacity determines  how many people a match contains.
    capacity: u8,
    /// A vector of ongoing matches.
    matches: Vec<Match>,
    /// HashSet indicating for each player which match they are in.
    players: HashMap<String, usize>,
}

I realised that this won’t work because if there are 3 matches (0, 1, 2) and I remove 1 because it ends, the players that used to point at 2 will be pointing outside the vector or to an incorrect match.

So I thought the obvious solution was to use a reference to the match: players: HashMap<String, &Match>. But this makes lifetimes very complicated.

What’s a good way to deal with a case like these where data are interrelated in the same struct?

#rust

threaded - newest

QT1@feddit.de on 25 Mar 2024 11:38 next collapse

You could store the matches in a HashMap as well, using some MatchId type as the key, i.e., HashMap<MatchId, Match>. Then you can use that as the reference in players: HashMap<String, MatchID>. Only downside is that you have to generate unique MatchIds, e.g., by using some counter.

5C5C5C@programming.dev on 25 Mar 2024 12:31 collapse

This is exactly the use case that slotmap is meant for. I highly recommend using the library rather than reinventing the concept.

modulus@lemmy.ml on 25 Mar 2024 15:10 collapse

Ah, that does seem like it will solve the problem. Thanks!

Gobbel2000@feddit.de on 25 Mar 2024 11:46 next collapse

I don’t think there is a good way of having references within the same struct, but you could store reference counted matches:

matches: Vec<Rc<Match>>,
players: HashMap<String, Rc<Match>>,

You would still have to make sure that the players map is updated, maybe weak references are useful there.

Maybe you could also consider storing the players of a match in the match itself, not outside.

modulus@lemmy.ml on 25 Mar 2024 12:14 collapse

Thanks, the RC is a possible approach. It seems to violate DRY a bit but maybe there’s no way around it.

The reason I had the players outside the match is that I need them there anyway, because when I get a player action I need to check in which match they are, who are their opponent(s) and so on. So even if they’re in, they’ll have to be out too as there are concurrent matches and the player actions come all through the same network stream.

expr@programming.dev on 25 Mar 2024 12:40 next collapse

Could you not have a hashmap keyed on matches pointing to vectors of strings for the players in each match? Basically modeling the data how you want rather than relying on indexing.

modulus@lemmy.ml on 25 Mar 2024 15:03 collapse

Not sure I understand. What I’m trying to do is something like this:

  • Poll a stream which takes fedi events. Read player commands.
  • If an event comes from a known player, check which match they are into.
  • With that info, get their opponents/coplayers etc and perform the change of state in the game (send replies, next turn, etc).

So what I have as a key is a player name (AP username) and from that I need to find which match they’re in.

There’s nothing semantically useful about a match ID.

expr@programming.dev on 25 Mar 2024 18:43 collapse

If you instead made your lobby field a HashMap<String, MatchId>, then whenever you get an event from a known player, you can lookup the match they are in from the lobby map and use that to lookup other data about the match (such as the other players) via the matches field (assuming that was also changed to be a hashmap as described in my previous comment).

[deleted] on 25 Mar 2024 13:15 collapse

.