39

I currently want to define a struct for a Piston game using GamerIterator:

pub struct MyGame<'a> {
    game_window: GameWindowGLFW,
    game_iter: GameIterator<'a, GameWindowGLFW>,
    //...
}

The GameIterator is generic in the GameWindow and its lifetime. I want to tell the compiler that it has the same lifetime as the field "game_window"/"the whole struct" and leave out the lifetime for the struct.

I also have difficulties initializing this:

MyGame {
    game_window: GameWindowGLFW::new(GameWindowSettings {/*...*/},
    game_iter: GameIterator::new(&mut game_window, &game_iter_settings), // game_window cannot be used here
    //...
}

I think that I can work around the initialization issue by using Option<GameIterator<...>> and an init() method, but I would like to avoid this because I can guarantee that game_iter is present after new() finishes.

What is the idiomatic way to write this?

nbro
  • 15,395
  • 32
  • 113
  • 196
maun
  • 393
  • 1
  • 3
  • 5

1 Answers1

19

Not only is there an issue with initialization, there could also be issues with destruction, if GameIterator implemented Drop: the compiler would have to know that it needs to destruct game_iter before game_window, otherwise game_window would have a reference to a destroyed GameWindowGLFW while running its drop() method.

There's no way to pass the lifetime of the struct itself as a lifetime argument. The only thing you can do is remove the game_window field from MyGame and pass a GameWindowGLFW instance to MyGame's initializer. If you want to encapsulate this so that the user doesn't need to create a GameWindowGLFW, you could write a method that creates a GameWindowGLFW and a MyGame on the stack and calls a closure that accepts a MyGame argument only.

pub struct MyGame<'a> {
    game_iter: GameIterator<'a, GameWindowGLFW>,
    //...
}

impl<'a> MyGame<'a> {
    fn new(game_window: &'a mut GameWindowGLFW) -> MyGame<'a> {
        MyGame {
            game_iter: GameIterator { game_window: game_window },
        }
    }
}

fn start_game(callback: |game: &mut MyGame|) {
    let mut game_window = GameWindowGLFW;
    let mut game = MyGame::new(&mut game_window);
    callback(&mut game);
}

fn main() {
    start_game(|game| {
        /* use game here */
    });
}
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • 4
    Thanks, I get the reason for this, but it is still strange that it does not work...Also can't think of a safe and generic solution the compiler could easily verify, all depends on the dependencies and initialization/destruction order. – maun Aug 21 '14 at 20:03