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

Я пытаюсь инициализировать структуру в два этапа, поскольку поля `window` и `app_data` не могут быть инициализированы в одной функции. Это связано с тем, что нельзя возвращать из функции ссылку на локальную переменную, и `app_data` зависит от `window`. На первый взгляд, это решение работает. Однако из-за реализации трейта `Drop` компилятор выдает ошибку о том, что вся структура живет недостаточно долго. 

```rust
struct Window;

struct Graphics<'window> {
    window_ref: &'window Window,
}

impl Drop for Graphics<'_> {
    fn drop(&mut self) {
    }
}

pub struct App<'app> {
    window: Window,
    app_data: Option<AppData<'app>>,
}

struct AppData<'graphics> {
    graphics: Graphics<'graphics>,
}

impl<'app> App<'app> {
    pub fn create_and_preinit() -> Self {
        let window = Window;

        Self {
            window,
            app_data: None,
        }
    }

    pub fn init(&'app mut self) {
        // Подготовка графики
        let graphics = Graphics {
            window_ref: &self.window,
        };

        self.app_data = Some(AppData {
            graphics,
        });
    }

    pub fn run(mut self) {}
}

fn main() {
    let mut app = App::create_and_preinit();
    app.init();
}
```

Как я могу разрешить проблему с временем жизни структуры, чтобы компилятор не жаловался на ее недостаточную продолжительность?
  • 5 сентября 2024 г. 9:03
Ответы на вопрос 2
Давайте посмотрим на вашу проблему с инициализацией структуры в два этапа, а именно на ошибки, связанные с временем жизни. Проблема возникает из-за того, что `Graphics` хранит ссылку на `Window`, которая имеет более короткое время жизни, чем сама структура `App`, когда вы присваиваете созданный объект `Graphics` полю `app_data`.

Когда вы создаете `Graphics`, он содержит ссылку на `window`, но при этом сам `app_data` имеет ссылку на `Graphics`. Поскольку `app_data` может существовать дольше, чем `window`, это вызывает конфликт во времени жизни.

Чтобы решить эту проблему, вам нужно изменить подход так, чтобы избежать хранения ссылок на локальные переменные, которые могут выйти за пределы своей области видимости. Одно из решений заключается в том, чтобы передать `window` в `Graphics` в виде ссылки или использовать обертку, такую как `Rc` или `Arc`, для управления временем жизни объектов.

Вот пример того, как вы можете это сделать с использованием `Rc`:

```rust
use std::rc::Rc;

struct Window;

struct Graphics {
    window_ref: Rc<Window>, // Используем Rc для управления временем жизни
}

impl Drop for Graphics {
    fn drop(&mut self) {
        // Освобождение ресурсов
    }
}

pub struct App {
    window: Window,
    app_data: Option<AppData>,
}

struct AppData {
    graphics: Graphics,
}

impl App {
    pub fn create_and_preinit() -> Self {
        let window = Window;

        Self {
            window,
            app_data: None,
        }
    }

    pub fn init(&mut self) {
        // Обертываем window в Rc
        let window_ref = Rc::new(self.window);

        // Создаем объект Graphics с использованием Rc
        let graphics = Graphics {
            window_ref: Rc::clone(&window_ref),
        };

        self.app_data = Some(AppData {
            graphics,
        });
    }

    pub fn run(self) {
        // Здесь можно запускать логику приложения
    }
}

fn main() {
    let mut app = App::create_and_preinit();
    app.init();
    app.run();
}
```

В этом примере `Window` оборачивается в `Rc` (объект с подсчетом ссылок), который управляет временем жизни. Теперь `Graphics` будет использовать `Rc<Window>`, что позволяет избежать ошибок времени жизни, так как `Rc` увеличит счетчик ссылок, пока существует как минимум одна ссылка на объект.

Таким образом, вы сможете выполнить инициализацию структуры в два этапа, не сталкиваясь с проблемами, связанными с временем жизни.
Тебе ссылка через & не подойдёт тут. 
Используй Arc и Weak.
Либо делай window тоже как ссылку на окно, которое создано в main.
Похожие вопросы