Как инициализировать поля структуры, которые ссылаются друг на друга

В настоящее время я хочу определить структуру для игры Piston с помощью GamerIterator:

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

GameIterator является общим в GameWindow и его жизни. Я хочу сказать компилятору, что он имеет такое же время жизни, что и поле "game_window"/"вся структура", и оставьте время жизни для структуры.

У меня также возникают трудности с инициализацией этого:

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

Я думаю, что я могу обойти проблему инициализации, используя Option<GameIterator<...>> и метод init(), но я хотел бы избежать этого, потому что я могу гарантировать, что game_iter присутствует после завершения new().

Каков идиоматический способ написать это?

Ответы

Ответ 1

Не только проблема с инициализацией, но также могут быть проблемы с уничтожением, если GameIterator реализован Drop: компилятор должен знать, что ему нужно разрушить game_iter до game_window, иначе game_window будет ссылаться на уничтоженный GameWindowGLFW при запуске его метода drop().

Невозможно передать время жизни самой структуры как аргумент lifetime. Единственное, что вы можете сделать, это удалить поле game_window из MyGame и передать экземпляр GameWindowGLFW в MyGame инициализатор. Если вы хотите инкапсулировать это, чтобы пользователю не нужно было создавать GameWindowGLFW, вы могли бы написать метод, который создает в стеке GameWindowGLFW и MyGame, и вызывает замыкание, которое принимает MyGame только аргумент.

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 */
    });
}