0Pricing
Learn Rust Coding · Урок

Управление состоянием приложения

Храните и обновляйте данные интерфейса.

«Управление состоянием приложения» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.

Состояние в структуре App

В egui состояние приложения — это обычные данные Rust, хранящиеся в структуре, реализующей eframe::App. Специального хранилища или модели, которой владеет фреймворк, нет.

Поля содержат всё: вводимые в форму данные, выбранные значения, загруженные данные и флаги. В каждом кадре update напрямую читает и изменяет эти поля через &mut self.

struct App {
    query: String,
    results: Vec<Item>,
    selected: Option<usize>,
    loading: bool,
}

Вывод реализации Default

eframe создаёт Ваше приложение в замыкании, переданном в run_native. Самый простой вариант — использовать #[derive(Default)] и создать приложение с помощью App::default().

Если начальные значения не должны быть стандартными, реализуйте Default вручную или добавьте конструктор, принимающий CreationContext для настройки, например шрифтов или сохранения данных.

impl App {
    fn new(cc: &eframe::CreationContext) -> Self {
        let mut app = Self::default();
        app.dark = cc.egui_ctx.style().visuals.dark_mode;
        app
    }
}

Единственный источник истины

Поскольку виджеты заимствуют &mut у соответствующих полей, структура всегда является единственным источником истины. Нет отдельного состояния представления, которое нужно поддерживать синхронным.

Флажок, связанный с &mut self.enabled, напрямую отражает это логическое значение и изменяет его — прочитайте его в любом другом месте того же кадра и увидите актуальное значение.

ui.checkbox(&mut self.enabled, "Enabled");
if self.enabled {
    ui.label("Feature is on");
}

Отложенные изменения

Есть одна тонкость с заимствованием: часто нужно пройти по Vec и одновременно удалить из него элемент. Нельзя изменять вектор, пока существует общее заимствование.

Решение — записать желаемое действие в локальную переменную во время цикла, а затем применить его после завершения цикла.

let mut to_remove = None;
for (i, item) in self.items.iter().enumerate() {
    ui.horizontal(|ui| {
        ui.label(&item.name);
        if ui.button("x").clicked() { to_remove = Some(i); }
    });
}
if let Some(i) = to_remove { self.items.remove(i); }

Экраны на основе перечисления

В приложениях с несколькими экранами представьте текущий экран полем-перечислением. Метод update сопоставляет с ним значение, чтобы определить, что рисовать.

Это делает навигацию явной и исчерпывающей: компилятор заставляет Вас обработать каждый экран, устраняя целые классы ошибок маршрутизации.

enum Screen { Home, Settings, About }

match self.screen {
    Screen::Home => self.draw_home(ui),
    Screen::Settings => self.draw_settings(ui),
    Screen::About => self.draw_about(ui),
}

Память egui для состояния интерфейса

Некоторое состояние относится только к интерфейсу, а не к Вашей предметной области: свёрнутые разделы, позиции прокрутки, состояние перетаскивания. egui хранит его в ctx.memory, используя идентификатор виджета Id как ключ.

Обычно Вам не нужно обращаться к этому напрямую, но ctx.data_mut позволяет сохранять небольшие значения для каждого идентификатора, которые сохраняются между кадрами и не загромождают структуру приложения.

let id = egui::Id::new("my_toggle");
let mut open = ctx.data_mut(|d| d.get_temp::<bool>(id).unwrap_or(false));
ui.checkbox(&mut open, "Open");
ctx.data_mut(|d| d.insert_temp(id, open));

Фоновая работа с каналами

Никогда не блокируйте update медленными операциями ввода-вывода — это замораживает цикл с частотой 60 кадров в секунду. Запустите поток и обменивайтесь данными через канал std::sync::mpsc.

Рабочий поток отправляет результаты обратно, а update каждый кадр извлекает их из приёмника без блокировки с помощью try_recv. Вызывайте ctx.request_repaint() из рабочего потока, чтобы интерфейс обновлялся при поступлении данных.

if let Ok(msg) = self.rx.try_recv() {
    self.results = msg;
    self.loading = false;
}

Запуск рабочего потока

Клонируйте Context и отправителя в поток. Клонирование выполняется быстро — внутри используется Arc — и позволяет рабочему потоку запрашивать перерисовку.

Переместите владение входными данными работы в замыкание, чтобы удовлетворить требованиям проверяющего заимствования и избежать проблем со временем жизни.

let tx = self.tx.clone();
let ctx = ctx.clone();
std::thread::spawn(move || {
    let data = expensive_load();
    let _ = tx.send(data);
    ctx.request_repaint();
});

Сохранение состояния

eframe может сериализовать состояние приложения между запусками. Включите возможность persistence, добавьте производные реализации serde::Serialize / Deserialize и реализуйте App::save.

При запуске прочитайте сохранённое состояние из cc.storage в конструкторе, используя значения по умолчанию, если ничего не сохранено.

fn save(&mut self, storage: &mut dyn eframe::Storage) {
    eframe::set_value(storage, eframe::APP_KEY, self);
}
// in new():
if let Some(s) = cc.storage {
    if let Some(app) = eframe::get_value(s, eframe::APP_KEY) { return app; }
}

Как не перегружать update

Поскольку update выполняется каждый кадр, не пересчитывайте в нём дорогие производные данные. Кэшируйте результаты в полях и пересчитывайте их только при изменении входных данных.

Распространённый подход: отслеживать флаг dirty, устанавливаемый методом Response::changed(), и перестраивать кэши в начале update только при установленном флаге.

if self.dirty {
    self.filtered = self.filter_items();
    self.dirty = false;
}
for item in &self.filtered { ui.label(&item.name); }

Владение обеспечивает предсказуемость

Главная идея такова: egui опирается на владение в Rust, а не на фреймворки для управления состоянием. Ваша структура владеет данными, &mut self предоставляет исключительный доступ на каждый кадр, а потоки обмениваются данными через каналы.

Здесь нет скрытых подписок или графов обратных вызовов, поэтому понять, когда и как меняется состояние, можно локально и явно.

Быстрая проверка

Как следует выполнять медленный сетевой запрос?

Итоги

Состояние приложения представлено обычными полями структуры eframe::App, которые изменяются через &mut self в каждом кадре. Поэтому структура является единственным источником истины.

Откладывайте изменения во время перебора, моделируйте экраны с помощью перечислений, выносите медленные операции в потоки и обменивайтесь данными через каналы, а состояние сохраняйте с помощью хранилища eframe. Поддерживайте update быстрым, кэшируя производные данные за флагами изменения. Далее: упаковка приложения для распространения.

Часто задаваемые вопросы

Урок «Управление состоянием приложения» бесплатный?

Да — полный текст урока «Управление состоянием приложения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.

Чему я научусь в уроке «Управление состоянием приложения»?

Храните и обновляйте данные интерфейса. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Learn Rust Coding?

Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Управление состоянием приложения»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Learn Rust Coding?

Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы интерфейса немедленного режима
  2. Виджеты и компоновка
  3. Управление состоянием приложения
  4. Сборка настольного приложения
← Назад к Learn Rust Coding