从 Rust 重写到 Zig,内存所有权变成了接口契约
据开发者 besok 的迁移记录,他把自己已有的 Rust JSONPath 实现重新写成 Zig,以比较语言和工程体验。作者有多年 Rust 背景,但这是其初次 Zig 项目;这是一份有具体例子的实践观察,不是语言性能排名,也不是迁移 Zig 的普遍建议。
作者:林岚|OC 开发者生态编辑
据开发者 besok 的迁移记录,他把自己已有的 Rust JSONPath 实现重新写成 Zig,以比较语言和工程体验。作者有多年 Rust 背景,但这是其初次 Zig 项目;这是一份有具体例子的实践观察,不是语言性能排名,也不是迁移 Zig 的普遍建议。
一句话结论:Zig 把内存与失败路径摆到接口表面,获得的直接控制,需要用明确所有权和测试来承担。
用熟悉的问题,减少比较中的噪声
重新实现已有项目有一个好处:开发者知道功能应当是什么,而不必同时探索业务需求和新语言。JSONPath 又有 RFC 9535作为查询语义参照,使“能运行一个示例”之外还有符合性目标。
但这仍不能把比较变成实验室控制试验。作者对两门语言的熟练程度不同,实现方法也会受旧习惯影响。某种写法让他觉得难读,首先说明这个迁移过程的摩擦,而不是所有团队都会得到同样结论。
这样的自我限定反而提高了记录价值。读者可以判断哪些经验适合自己,而不必先接受“下一代 C”或“谁替代谁”的口号。
分配器参数,意味着谁来支付与收尾
在 Zig 中显式传递分配器,让代码使用哪种分配策略更清楚。短命任务可以考虑集中管理临时内存,长寿命对象则需要明确何时释放。控制权更多,接口也更需要把生命周期说清楚。
Zig 文档提供 defer、errdefer 等机制帮助安排退出时的清理。但这些工具不会替开发者设计所有权:一个对象交给缓存后,原调用方是否还负责释放,仍要由程序约定。
最危险的情况不一定是忘了写释放,而是两层代码都以为自己负责。浅拷贝复制了指针,却没有复制一份可以各自销毁的资源。代码看起来多了一个变量,真实资源可能仍只有一份。

正常成功路径,覆盖不了分配失败
一次操作常包含多次分配:先复制字符串,再扩容列表,再把对象加入结果。如果最后一步失败,前面已成功分配的内容由谁回收?
作者记录的测试经验,正是把失败放到过程内部,而不是只检查最终返回成功。OC 认为,这对所有显式资源管理代码都有借鉴意义;文件句柄、网络连接和事务也会出现“前一步成功、后一步失败”的半成品状态。
因此,测试不应只有正常输入,还应有逐步失败、提前返回和所有权转交之后的清理。检测器能够发现被执行路径中的问题,却不能替代没有写出来的测试路径。
语言之外,依赖会决定实现边界
一个标准查询语言实现,可能需要 Unicode、正则表达式和错误报告。核心解析器写得顺利,不代表外围依赖已经覆盖标准要求。缺失的能力会变成团队自己维护的工作。
迁移评估也应检查编辑器、构建、调试和持续集成,而不是只比较代码行数。标准库演进可能带来更好接口,也意味着版本升级要付迁移成本。固定工具链和符合性测试,有助于把这种成本从突发故障变成可计划的工作。
Rust 在安全代码里用所有权检查承担了一部分约束,Zig 更依赖显式管理与工程纪律。它们是在不同位置安排复杂性,而不是一方有复杂性、另一方没有。选择之前,团队应先问自己最需要控制什么,又有能力长期维护哪些保证。
关键事实
- 实践对象:已有 Rust JSONPath 库的 Zig 重写。
- 比较性质:作者个人工程记录,非跨语言统一性能测试。
- 技术重点:分配器、清理路径、资源交接与符合性测试。
- 适用边界:工具链版本、依赖成熟度和团队经验都会改变结果。
OC 判断
林岚认为,这份记录的价值在于展示责任怎样移动。少一些编译器约束,不代表少一些约束;它们可能转移到接口文档、代码审查和失败注入测试里。能否承受这份责任,比语言偏好更能决定项目成败。
为什么重要
- 对开发者:资源归属应成为接口契约,而非口头习惯。
- 对团队:把失败路径和标准符合性纳入迁移成本。
- 对开源维护者:声明工具链和已知缺口,能减少用户误把可用示例当完整实现。
评论
围绕这篇文章补充信息、提出问题或分享观察。