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源码中的例子:使用LazyLock和Regex的静态变量,其分配的内存(可能至少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
社区学习交流平台订阅:
评论区
写评论还没有评论