< 返回版块

Mike Tang 发表于 2026-03-07 15:03

Ply:一个用Rust构建应用的新框架

作者想用Rust开发一款多人棋盘游戏(包含服务器、客户端和共享游戏逻辑),但发现现有框架都存在问题,最终决定自己开发Ply框架。

现有Rust框架的问题

  • Bevy:强制使用ECS架构,UI系统基于宏和节点,代码冗长,充斥着impl Bundle..default()
  • Iced:代码中到处是..default().into(),嵌套结构不清晰,代码阅读顺序颠倒
  • egui:需要手动调用.add_space()和分配矩形,简单UI可以,复杂应用很累
  • Slint:虽然嵌套清晰,但使用独立的标记语言,无法与Rust代码干净集成
  • macroquad:只是渲染库而非应用引擎,缺少布局系统、文本输入等UI结构

Clay的尝试与失败

作者尝试使用Clay(C语言布局库)配合macroquad,创建了Clayquad:

  • 初期开发速度不错
  • 后来出现大量bug:内存泄漏、竞态条件、无纹理管理
  • 功能限制:不支持着色器、旋转、滚动需手动实现、无文本输入

Ply的诞生

开发过程:

  • 2025年底持续在Clayquad上添加功能
  • 2月开始重写:将布局引擎完全移植到Rust
  • 实现新API设计,添加着色器、可访问性、CLI、网络等功能

核心设计:

  • 语法:构建器模式 + 闭包
  • 类型转换:大量使用Into<T>,如.background_color()接受多种颜色格式,.image()接受多种图片来源
  • 便利性use ply_engine::prelude::*导入所有内容

设计哲学

核心原则: 在提供完全控制的同时降低使用难度

即时模式(Immediate-mode)优势:

  • 每帧重建UI实际上比跟踪变化更快
  • 布局计算只占帧时间的极小部分
  • 无论如何都需要每帧重绘
  • 提供对渲染内容和时机的完全控制

https://plyx.iz.rs/blog/introducing-ply/

Rust动态库中的资源泄漏问题

核心问题

作者发现Rust中静态变量的设计使得动态库卸载时很容易发生资源泄漏。由于第三方crate广泛使用全局变量,这导致无法在不产生内存泄漏的情况下卸载使用第三方crate的Rust代码。

问题背景:为什么这很重要?

作者来自Windows底层开发背景,在以下场景中动态模块的正确清理至关重要:

  • 内核模块需要支持无内存泄漏的卸载
  • 用户态动态库在卸载时不应留下泄漏
  • 高级应用场景:需要在不终止主进程的情况下远程升级小型动态库

C++中的全局变量清理

  • C++通过编译器/操作系统特定机制来析构全局变量
  • 但在进程退出时等待优雅终结耗时很长
  • 操作系统会自动释放内存,无需手动释放数千个堆分配
  • 堆损坏问题可能在释放时才暴露
  • 微软甚至实现了"容错堆"(Fault Tolerance Heap)来故意忽略主函数结束后的释放调用

Rust的设计选择

Rust刻意避免释放全局变量

  • 所有全局变量具有'static生命周期,永不调用drop方法
  • 理由是程序终止时操作系统会释放所有资源
  • 但这个理由不适用于动态库场景——进程继续运行,操作系统不会释放资源

问题示例

文中给出rustdocs源码中的例子:使用LazyLockRegex的静态变量,其分配的内存(可能至少1KB)永远不会被释放。

作者的观点

对于一门系统编程语言,这种设计是有问题的。开发者在使用数百个第三方crate的用户态环境中,实际上没有合理的方法在可卸载的上下文中使用Rust。

在Linux内核或Windows内核驱动等特定环境中,可以通过no-std和限制内核中的Rust代码来缓解这个问题。

https://old.reddit.com/r/rust/comments/1rmhnuz/is_it_possible_to_create_a_nonleaking_dynamic/

--

From 日报小组 Mike

社区学习交流平台订阅:

评论区

写评论

还没有评论

1 共 0 条评论, 1 页