十年 Rust 老将,痛心揭示其“三宗罪”
2024-09-22 【 字体:大 中 小 】
尽管 Rust 以其强大的内存安全性、零成本抽象以及出色的并发支持赢得了广泛赞誉,但在长期的使用过程中,一位在 Rust 世界深耕了十年之久的资深开发者表示,他遇到了一些让他感到失望的地方。
作者 | Lord_Zane 翻译 | 郑丽媛
到目前为止,我已经使用 Rust 大约 10 年了,从 2015 年 5 月 Rust 1.0 发布前不久就开始了。这些年来,我用 Rust 开发过各种项目,包括桌面 GUI 应用、服务器后端、命令行程序、通过 WebAssembly 实现沙盒脚本接口,以及多个与游戏相关的项目。就在最近,我还对 Bevy 游戏引擎做出了大量贡献。
除了 Rust,我还掌握了其他几种编程语言:Java、Python、TypeScript、Elixir、C 语言等,还有一些较为小众的语言,但相关的开发经验也相对较少。虽然不能说我在这些语言上是专家,但大体上我也很熟悉它们并了解其主要特点。在本文中,我主要会将 Rust 与 Java 进行比较,因为这是我除了 Rust 之外最常用的语言。
提前声明一下:在所有这些语言中,我最喜欢 Rust,之后也并不打算改变这个想法。我每天都在用它编程,大部分时间里都感到非常愉快。但就像其他语言一样,Rust 也有一些问题,而我想讨论的是那些反复出现的重大痛点。这些问题显著影响了我的工作效率,除非进行根本性改变,否则无法解决。
以下是一些我不会涉及的问题列表:
异步/等待(Async/Await):在我看来,Rust 中的 Async/await 其实相当不错。在没有额外开销或内置运行时、取消操作等限制条件下,它相当可靠。我记得在 Rust 2018 版本发布时,为了引入这一特性遇到了很大压力,但我认为最终结果还是很不错的。主要问题在于同步代码和异步代码的混合使用、Pin 特性、生态系统中的多种执行器,以及零成本是否真的值得权衡。这个问题已经被讨论了很多次,我没什么可补充的。也许虚拟线程会更好一些,即直接承担运行时的成本,但我也不确定。我觉得,现在我们在 Web 服务器等场景下单独使用异步特性已经相当稳固了。
库生态系统:我希望它能更稳定、更少 bug(例如,将 winit 与 sdl 进行对比),但这并不是语言本身的问题。关于这一点,我没有太多可以讨论的内容。
现在,来谈谈我对 Rust 的一些意见吧。
Result
当我刚开始接触 Rust 时,我很喜欢错误处理只是另一种类型的设计理念:隐式错误非常讨厌;强制用户意识到一个函数可能会出错,并处理这个错误是一个很棒的设计!
然而,随着我多年来在库和应用代码中都使用了 Rust,我对这种设计理念越来越失望。
作为一个库的作者,必须为每一个可能出现的问题创建新的错误类型、并在它们之间进行转换,真是让人头疼。最糟糕的情况莫过于你添加一个依赖项、调用其中的一个函数,然后还得想办法把它的错误类型添加到你的包装错误类型中。像 thiserror 这样的库可能稍微好点,但仍然不是很好的体验。这还只是调用一个函数的情况——如果你写了第二个不同功能的函数,可能就又需要一个全新的错误类型了。
然后是应用代码。通常情况下,我们不会关心一个函数失败的具体原因或方式,只想把错误上传并向用户显示最终结果。当然,有 anyhow 这样的库可以部分解决这个问题,但根据我的经验,Java 在这方面处理得更好。除了明显想要有一个动态分派类型的问题外,对我来说真正的问题是回溯信息。
在 Java 中,我可以看到一个完美的日志记录,确切地知道哪个函数首先抛出了错误,以及这个错误是如何传播到程序的日志记录或显示机制中的。而在 Rust 中,每当你使用 ? 运算符传播错误时,是没有回溯信息的。当然,回溯信息会有性能开销,这也就是为何它不是内置功能的原因。
库也会遇到这个问题——当用户报告一个 bug 时,很难搞清楚具体的问题所在,因为你所得到的只是“顶层函数失败”的信息,没有回溯信息,除非它是 panic。同样,追踪依赖项自身为什么会抛出错误也很困难。
Rust 在“迫使开发者考虑错误”这一点上做得很好。与 Java 不同,一个函数可能会失败这一点在 Rust 中非常明确,你不可能不小心忽略这个问题。我在其他语言中见过很多 bug,例如某个函数抛出了错误导致整个程序崩溃,而它本应该在更低层级通过重试等方式来处理。
然而,尽管它是零成本且非常明确的,但我认为 Rust 在认为人们会关心一个函数失败的具体原因方面犯了一个错误。我真的认为 Rust 该标准化一个单一的类型,类似于 Box(包括支持字符串错误),并当错误在函数之间传播时自动附加上下文。这不适用于所有情况,因为它不是零成本的,也不是那么明确,但对于很多情况来说是有意义的。
顺便提一下,还有错误信息的问题。错误信息应该格式化为“Error: Failed to do x.”还是“Failed to do x”?是否需要句号结尾?首字母大小写?这不完全是语言本身的问题,但我希望有一个全生态系统的标准来格式化错误信息。
模块系统
有时孤儿规则确实令人头疼,而模块系统可能过于灵活了。
(注:孤儿规则是 Rust 中的一个规定,简称 OR。当为某类型实现某 trait 时,要求类型或 trait 至少有一个是在当前crate中定义的,不能为第三方的类型实现第三方的 trait。以此确保他人编写的代码不会破坏你的代码,也能避免你不小心破坏了其他不相关代码的情况。)
在维护 Bevy 项目时,我对此深有感触。Bevy 是一个单仓库项目,包含 bevy_render、bevy_pbr、bevy_time、bevy_gizmos、bevy_ui 等多个模块,还有一个顶层的 bevy 包用来重新导出所有内容。
在 crate 之间组织代码相当困难。你可以随意在 crate 之间重新导出类型,将某些部分设为 pub(crate)、pub(super) 或 pub(crate::random::path)。对于导入,同样的问题也适用,你可以选择重新导出特定的模块或类型。这样很容易无意中暴露你不打算公开的类型,或者重新导出一个模块时丢失了为其编写的模块文档。
这不仅仅是一个实际问题,而是因为模块系统赋予了过多的灵活性。这很奇怪,因为 Rust 喜欢显式,但在如何安排类型方面却给了你很大的自由度。无论你对 Java 的“一个文件就是一个类;模块路径遵循文件系统文件夹”的做法有何看法,至少它是明确的。在 Java 中进入一个大型项目并准确知道某个类型的位置要比在 Rust 中容易得多。
孤儿规则也是一个问题,但这个问题我没有太多要说的。即使对于库开发者来说,由于一个项目需要跨 crate 拆分(而 Rust 鼓励你将东西拆分成多个 crate)也会遇到不少麻烦。
编译时间和 IDE 工具
我觉得,编译时间和 IDE 中的错误检查太慢了。尽管许多人在加快 rustc 和 rust-analyzer 方面做了很多出色的工作,我也无意贬低他们的努力,但从根本上说,Rust 将 1 个 crate 视为 1 个编译单元,这确实损害了最终用户的体验。在 Bevy 的 monorepo 中触碰一个函数意味着整个 crate 都会被重新编译,以及所有依赖它的其他 crate。我真的希望修改一个函数实现/文件,就像重新编译那个函数/文件并修补二进制文件一样简单。
Rust analyzer 也有同样的问题。IntelliJ 在启动时会对我的项目进行一次索引,并在剩余的开发时间里立即显示错误。而 Rust analyzer 则感觉每次你输入时都在重新索引整个项目。对于小项目来说还好,但在 Bevy 这样的规模上几乎无法使用。
我不是一个编译器开发者——也许这些都是无法解决的基本问题,尤其是考虑到宏、编译脚本、cargo 特性和其他问题。但我真的希望编译器能够维护一个项目结构图,并检测到我只修改了这一部分。既然用 VDOM 进行 UI 开发时能做到这一点,那么在 cargo/rustc 中怎么就不能实现呢?
这篇文章就到这里了。以上我所说的一切可能都只基于表面,没有深入研究过这些问题的现有讨论。尽管如此,这些是过去几年困扰我的主要问题,我很想听听其他人是否也面临同样的问题。
猜你喜欢
中国三三传媒公布2023年业绩 收入约34608万元同比减少约513%
年初复苏希望破灭,德国2月消费者信心加重疲软,节约意愿创十五年新高
注意阶段有分歧,及风格切换扰动
张雨霏:遇到困难想放弃很容易,但坚持下来才是最酷的
研究显示美国2023年罢工人数接近54万,增长141%
新股解读丨靠“渣渣辉”一年豪赚88亿,中旭未来的“贪玩游戏”还能玩多久?
银价短线突然跳水!白银日内交易分析:银价逼近关键水平 跌破恐令前景转为看跌
世界黄金协会:各经济体央行2023年全年净购金量为1037吨
欧洲央行管委De Cos:欧元区经济增速仍有偏向下行的风险
香港证券业重磅发声:撤销股票印花税
亿都(国际控股)(00259)发布中期业绩 股东应占溢利113亿港元 同比减少6194%
保诚(02378):委任花旗前高管伍燕仪为大中华区、客户及理财管理区域执行总裁
2024年2月12日全国主要批发市场珍宝蟹价格行情
按下加速键!金融机构年末密集转让不良贷款
凌钢转债上涨037%,转股溢价率5923%
美光英睿达T705 PCIe 50 SSD曝光,连续读取速度达145 GBs
极氪(ZKUS)在美国纽交所敲钟上市 成为最具全球价值的中国新能源题材
金科地产集团股份有限公司成被执行人,被执行金额1000000元
不到3年 新一轮中央常规巡视再次覆盖10多家金融单位
华东师范大学学科教学(物理)专业考研复试分数线内容时间招生数(24复试参考)
时习之丨把生态文明建设这篇大文章做好 习近平作出深刻阐释
巴黎奥运:群魔乱舞的开幕式实为西方文明的追悼会
粤海饲料最新公告:2024年上半年净亏损479774万元
泸州老窖新提交6件商标注册申请
鼎旭投资郭海涛:相信专精特新的力量,坚持投资与孵化“双轮驱动”
福晶科技:目前正在按计划建设中,尚未完工
今年为止,最爱的免签国家出现了
1964年,邓小平来到兰州铀浓缩工厂视察,突然一个熟悉的身影
近700家股权私募“埋伏”科创板 投资105家企业深创投领投10家
阿勒泰的夏牧场 美出天际