0Pricing
Learn Rust Coding · 课时

管理应用状态

保存并更新 UI 数据。

管理应用状态 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。

App 结构体中的状态

在 egui 中,应用状态就是存放在实现了 eframe::App 的结构体上的普通 Rust 数据。不存在特殊的存储区,也不存在由框架拥有的模型。

字段保存所有内容:表单输入、选择项、已加载数据和标志位。每一帧中,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 Memory

有些状态纯粹与界面有关,而不是您的业务领域——例如折叠区域、滚动位置和拖动状态。egui 会将这些状态存储在 ctx.memory 中,并以控件 Id 作为键。

您很少需要直接操作它,但 ctx.data_mut 可以让您存放少量按 ID 区分的值。这些值会在帧之间持续存在,又不会让您的应用结构体变得杂乱。

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 每帧都会运行,请避免在那里重复计算昂贵的派生数据。将结果缓存到字段中,并仅在输入发生变化时重新计算。

一种常见模式是:通过 Response::changed() 设置一个 dirty 标志,并仅在该标志有效时,于 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 保持轻量。接下来:为发布打包您的应用。

常见问题解答

「管理应用状态」课时是免费的吗?

是的 — 「管理应用状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。

「管理应用状态」这节课中我会学到什么?

保存并更新 UI 数据。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Learn Rust Coding 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「管理应用状态」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Learn Rust Coding 课中编写并运行代码吗?

能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 即时模式 UI 基础
  2. 组件与布局
  3. 管理应用状态
  4. 打包桌面应用
← 返回 Learn Rust Coding