2025 年 7 月 3 日
《Introducing tmux-rs》
作者:Collin Richards
约 6 个月来,作者将 tmux 从 C 语言移植到 Rust 语言,现已完成 100%(不安全)Rust 代码基础,分享从约 67,000 行 C 代码到约 81,000 行 Rust 代码(不包括注释和空行)的移植过程。
- Starting with C2Rust:以使用C2Rust工具开始项目,其输出代码虽能运行但难以维护且比原 C 代码大 3 倍,作者手动重构后仍需参考原 C 代码,最终放弃 C2Rust 输出,手动将文件从 C 翻译为 Rust。
- Build process:重写的重要部分是理解项目的构建方式,使用
autotools
,通过build.sh
脚本结合cargo
和make
进行构建,早期尝试将代码拆分为小 crate 但发现将所有代码放在一个 crate 更简单,后来改为每次翻译一个函数并进行验证,还使用cc
crate 构建 Rust 二进制文件并链接 C 库。 - Interesting bugs:在翻译代码过程中引入了许多 bug,如函数翻译后程序出现段错误,原因是 C 代码使用了隐式声明导致类型不匹配;另一个 bug 是手动翻译结构体类型声明时遗漏了
*
,导致 C 和 Rust 代码对类型的看法不同。 C Patterns in Rust:
- Raw pointers:Rust 有两种引用类型
&T
和&mut T
,在将 C 代码移植到 Rust 时,由于 Rust 引用的不变量要求,不能直接使用 Rust 引用,而需使用*mut T
和*const T
原始指针。 - Considering Goto:C 中的
goto
在 tmux 代码库中的使用较为温和,c2rust transpiler 用算法模拟goto
逻辑,多数情况下可使用更简单的方法实现goto
的跳转,如向前跳使用带有break
语句的标签块,向后跳使用带有continue
的标签循环。 - Intrusive Macros:Tmux 大量使用通过宏定义的两种数据结构,作者通过多次迭代实现了模仿 C 代码的良好 Rust 接口,遇到的挑战是处理一些实例可同时存在于不同容器的情况,通过使 trait 泛型来解决。
- Yacc shaving:Tmux 使用
yacc
实现自定义配置语言解析器,作者最终使用lalrpop crate实现解析器,结构与yacc
相似,还需实现适配器将 lalrpop 与自定义词法分析器接口。
- Raw pointers:Rust 有两种引用类型
Development process:
- Vim:在项目中使用 neovim 并依赖自定义宏加速翻译过程,如将
ptr == NULL
转换为ptr.is_null()
等。 - AI Tools:在开发后期尝试使用 Cursor,但感觉它没有增加速度,只是节省了手指疼痛,因为它会偶尔插入 bug,需要花费时间审查生成的代码,不过如果手指真的受伤,仍会使用它。
- Vim:在项目中使用 neovim 并依赖自定义宏加速翻译过程,如将
- Conclusion:虽然代码已 100%为 Rust,但作者认为未达到主要目标,手动翻译的代码不比 C2Rust 输出好多少,且容易崩溃,下一个目标是将代码库转换为安全 Rust,已发布 0.0.1 版本供其他 Rust 和
tmux
爱好者使用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。