0

I'm trying to represent a basic game in Rust, in which there are multiple players and each of those players is in a team. I'm trying to represent this in Rust, but struggling with the borrow checker. What I've got so far is a setup where

  1. the Game struct owns the players and the teams (allowing me to easily perform operations over Players or over Teams, and then
  2. the Players have a reference to their Team, and
  3. each Team has a vector of references to their Players:
pub struct Game<'a> {
    players: Vec<Player<'a>>,
    teams: Vec<Team<'a>>,
}

pub struct Player<'a> {
    team: Option<&'a Team<'a>>,
}

pub struct Team<'a> {
    players: Vec<&'a Player<'a>>,
}

I'm writing the game as a telegram chat bot, so using teloxide I've got a bunch of handlers and then a DB which persists the entire Game struct. Each time a handler is called, the Game struct is recreated from the DB, and is potentially mutated to add new players or teams. Something like:

pub fn handler1() {
    let mut g = get_game_from_db();
    let p = Player::new();
    g.players.push(p);

    let t = Team::new();
    g.teams.push(t);

    write_game_to_db(g)
}

But the issue comes when I want to add a new team and a new player, and then add that player to the new team. I tried to do this, but the borrow checker complained:

    1 pub fn handler2() {
    2     let mut game = get_game();
    3     // Add a player to the game
    4     game.players.push(mock_player());
    5     // Add a team to the game
    6     game.teams.push(mock_team());
    7
    8     // Borrow the team
    9     let team = &mut game.teams[0];
   10     // Borrow the player
   11     let player = &mut game.players[0];
   12
   13     // Add the team to the player
>> 14     player.team = Some(team); // immutable borrow occurs here
   15     // Add the player to the team
>> 16     team.players.push(player); // cannot borrow `team.players` as mutable because it is also borrowed as immutable \ mutable borrow occurs here|immutable borrow later used by call
   17 }

The full error is:

error[E0502]: cannot borrow `team.players` as mutable because it is also borrowed as immutable
  --> src/so.rs:65:5
   |
63 |     player.team = Some(team);
   |                        ---- immutable borrow occurs here
64 |     // Add the player to the team
65 |     team.players.push(player);
   |     ^^^^^^^^^^^^^----^^^^^^^^
   |     |            |
   |     |            immutable borrow later used by call
   |     mutable borrow occurs here

Is there any way to represent this sort of relationship in Rust?

Aleksander Krauze
  • 3,115
  • 7
  • 18
beyarkay
  • 513
  • 3
  • 16
  • 1
    In short: what you're trying to build is called a self-referential struct, and is known to be very hard in Rust. – Chayim Friedman Mar 10 '23 at 08:37
  • I think this question should be reopened. While it's true OP originally asked for how to create a self-referential struct just stating this fact doesn't help in any way. The real question here is *how* to change this model, such that we don't need to do a self-referential structs. And as it happens, it's quite easy to do. You just need to use `Rc` and `Weak`. – Aleksander Krauze Mar 10 '23 at 09:31
  • 2
    @AleksanderKrauze This is mentioned (in one sentence) in the duplicate, in the section "How do I fix it?" – Chayim Friedman Mar 10 '23 at 09:46
  • For what it's worth, I would very much like to have a code example of the idiomatic way of using `Rc` and `Weak` to fix this problem. I have no idea where I would get started. – beyarkay Mar 11 '23 at 12:47

0 Answers0