0

I cannot figure out how to make the following code compile successfully. The first step was struggling through the lifetime annotation, however, I feel that it finally worked out.

The next step is the borrowing and lifetime around the HashMap entry. I feel I am already too deep into the rabbit hole and need some guidance to get out.

use std::collections::HashMap;

struct Player {
    id: u32,
}

struct Game<'a> {
    black: &'a Player,
    white: &'a Player,
    win: bool,
    timestamp: u32,
}

struct Base<'b> {
    games: &'b mut Vec<Game<'b>>,
    players: &'b mut HashMap<u32, Player>,
}

impl<'c> Base<'c> {
    fn create_game(self, black_id: u32, white_id: u32, win: bool, timestamp: u32) -> &'c Game<'c> {
        let black_player = self
            .players
            .entry(black_id)
            .or_insert(Player { id: black_id });
        let white_player = self
            .players
            .entry(white_id)
            .or_insert(Player { id: white_id });

        let game = Game {
            black: &black_player,
            white: &white_player,
            win: win,
            timestamp: timestamp,
        };

        self.games.push(game);
        &self.games[0]
    }
}
error[E0499]: cannot borrow `*self.players` as mutable more than once at a time
  --> src/lib.rs:25:28
   |
19 |    impl<'c> Base<'c> {
   |         -- lifetime `'c` defined here
20 |        fn create_game(self, black_id: u32, white_id: u32, win: bool, timestamp: u32) -> &'c Game<'c> {
21 |            let black_player = self
   |   ____________________________-
   |  |____________________________|
   | ||
22 | ||             .players
   | ||____________________- first mutable borrow occurs here
23 | |              .entry(black_id)
   | |_____________________________- argument requires that `*self.players` is borrowed for `'c`
24 |                .or_insert(Player { id: black_id });
25 |            let white_player = self
   |  _____________________________^
26 | |              .players
   | |_____________________^ second mutable borrow occurs here

error[E0597]: `black_player` does not live long enough
  --> src/lib.rs:31:20
   |
19 | impl<'c> Base<'c> {
   |      -- lifetime `'c` defined here
...
31 |             black: &black_player,
   |                    ^^^^^^^^^^^^^ borrowed value does not live long enough
...
37 |         self.games.push(game);
   |         --------------------- argument requires that `black_player` is borrowed for `'c`
38 |         &self.games[0]
39 |     }
   |     - `black_player` dropped here while still borrowed

error[E0597]: `white_player` does not live long enough
  --> src/lib.rs:32:20
   |
19 | impl<'c> Base<'c> {
   |      -- lifetime `'c` defined here
...
32 |             white: &white_player,
   |                    ^^^^^^^^^^^^^ borrowed value does not live long enough
...
37 |         self.games.push(game);
   |         --------------------- argument requires that `white_player` is borrowed for `'c`
38 |         &self.games[0]
39 |     }
   |     - `white_player` dropped here while still borrowed
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
eric
  • 1,857
  • 5
  • 23
  • 33
  • 1
    Your code will never work as, it has too many references. Rust is not C, you cannot just store references here and there and hope it will compile. The obvious workaround is to use `Rc>` everywhere, but this way, while ok for porting C code, leads to madness! – rodrigo May 05 '20 at 21:56
  • Thanks for the candid feedback. I guess one can tell my background :-). My main goal was simply to have a Game reference the two participating Players (the references in Base - I guess - are just a result of my trial & error). I am curious would that already be considered "madness" in a language like Rust? – eric May 05 '20 at 22:14
  • Your code is not "madness" in my book, because it just does not compile, and that is quite expected in Rust, as you probably noticed. Using `Rc/Weak>` indiscriminately lets you store references where-ever you want, and get mutations when you need them... but then you will have random panics when you happen to borrow the same object twice in two unrelated pieces of code. – rodrigo May 05 '20 at 22:23

1 Answers1

2

If you just want to make the code "compile" (and in a sense "usable for further development"), and not trying to "learn how to do complicated life-cycle in Rust", then I'd suggest this following simpler version of your code.

#[derive(Copy, Clone)]
struct Player {
    id: u32,
}

#[derive(Copy, Clone)]
struct Game {
    black: Player,
    white: Player,
    win: bool,
    timestamp: u32
}

struct Base {
    games: Vec<Game>,
    players: HashMap<u32, Player>
}

impl Base {
    fn create_game(&mut self, black_id: u32, white_id: u32, win: bool, timestamp: u32) -> Game {
        self.players.entry(black_id).or_insert(Player {id: black_id });
        self.players.entry(white_id).or_insert(Player {id: white_id });
        let black_player: &Player = self.players.get(&black_id).unwrap(); // Should be safe to unwrap here
        let white_player: &Player = self.players.get(&white_id).unwrap(); // Should be safe to unwrap here

        let game = Game {
            black: *black_player,
            white: *white_player,
            win: win,
            timestamp: timestamp
        };

        self.games.push(game);
        self.games[0]
    }
}
Dat Nguyen
  • 1,626
  • 22
  • 25