GUI 依赖图的另一种答案:用 Rust 的 Drop 做零成本清理
内容提要
Burin 是一个基于 Rust 所有权、Drop 和 async 构建的 GUI 框架,通过 Signal 订阅闭包直接标记脏元素,实现 O(1) 更新,避免运行时依赖追踪。它纯 Rust 编写,无 DSL,提供 60 个内置 Widget、双渲染后端和 Material 3 主题,但早期阶段,不适合快速原型或嵌入式场景。
延伸解读
核心机制:O(1) 脏标记
Burin 通过 Signal 订阅闭包直接标记脏元素,避免了运行时依赖追踪。每次状态更新只需 O(1) 时间定位脏元素,再通过 O(k) 祖先上行处理,配合 SubtreeCache 跳过未变更子树,实现高效增量渲染。这种设计让更新路径变得可预测,减少了不必要的计算开销。
与主流框架的对比
Burin 与 Flutter、Slint、Xilem 在脏标记模型上殊途同归,但实现方式不同:Flutter 依赖庞大工程团队,Slint 依赖 DSL 编译器,而 Burin 利用 Rust 的 ownership 和 Drop 在编译期静态化订阅关系,无需运行时追踪。这体现了语言特性对框架设计的影响,也展示了 Rust 在 GUI 领域的潜力。
适用场景与局限
Burin 目前处于早期阶段,适合对 Rust 所有权模型熟悉的开发者探索。它不适合快速原型(Egui 更合适)、嵌入式(Slint 更优)或已有跨平台团队的项目。其优势在于纯 Rust 实现、无 DSL、IDE 支持好,但生态和成熟度仍需时间检验。
Q&A
Burin 框架的核心机制是什么?
Burin 的核心机制是将 Signal 的订阅闭包与 Element 的脏标记直接绑定,当 Signal 写入时,闭包直接标记对应元素为脏,实现 O(1) 更新,无需运行时依赖追踪。
Burin 如何利用 Rust 的 Drop 实现自动清理?
Burin 中每个 Element 持有 LifecycleComponent,其中存储所有订阅句柄。当元素从 arena 移除时,LifecycleComponent 被 Drop,所有订阅闭包自动退订,无需手动取消订阅或 GC。
Burin 与 Flutter、Slint、Xilem 在脏标记处理上有何异同?
Burin 与 Flutter、Slint、Xilem 都采用类似的脏标记模型:O(1) 标记 + O(k) 处理。区别在于实现方式:Flutter 靠工程团队迭代,Slint 靠 DSL 编译器,而 Burin 用 Rust 的 Signal + ownership + Drop,代码量仅 15 万行。
Burin 的代码编写有什么特点?
Burin 是纯 Rust 编写,无 DSL、无宏 DSL、无代码生成,因此 IDE 的补全、跳转定义、重构等功能开箱即用。
Burin 目前提供了哪些功能?
Burin 提供 60 个内置 Widget(布局、输入、显示、覆盖层等)、双渲染后端(wgpu 和 tiny-skia)、手势竞技场(7 种 Recognizer)、Material 3 主题、TestHarness 和 DevTools。
Burin 不适合哪些场景?
Burin 不适合快速原型(建议用 Egui)、嵌入式/MCU(建议用 Slint)、已有 Dart/JS 团队的跨平台项目(建议用 Flutter/React Native)。
Burin 的渲染管线是怎样的?
Burin 的渲染管线为:Signal::set() → register_dirty(O(1)) → process_dirty_set(O(k) 祖先上行) → Taffy 增量布局 → SubtreeCache 检查 → 仅绘制脏子树 → GPU (wgpu) 或 CPU (tiny-skia)。每层都知道什么变了,未变的部分完全跳过。