后端选 Rust 还是 Go:我们如何选择
两者都是现代、快速的编译型语言。真正的问题不是哪个“更好”,而是哪个适合你眼前的问题。下面是我们在实践中做决定的方式。
我们同时用 Rust 和 Go 构建生产系统,我们自己的产品也同时用这两种语言:迭代速度要紧的地方用 Go,时延和内存安全不容妥协的地方用 Rust。所以当有人问新后端该用哪种语言时,我们的回答是:看情况。任何只用一个词回答你的人,都是在推销什么东西。
这篇文章是我们希望更多人去做的那种比较:不比基准测试,只谈在真实项目中决定选择的实际权衡。
它们的共同点
先从共识说起,因为共识很多。两者都编译为原生代码,都快到语言本身很少成为瓶颈,都能产出一个在容器中轻松部署的单一静态二进制文件,也都有一流的并发支持和健康的生态。任选其一,都是做后端服务时站得住脚的选择。就算选了“错”的那个,你的后悔程度也远不会像网上争论暗示的那么大。
Go 胜在哪里
Go 最看重的只有一件事:让团队高效,并保持高效。这门语言小到一周就能学会,标准库覆盖了服务所需的大部分功能,goroutine 让并发代码变得平易近人,而不是令人望而生畏。编译几乎瞬间完成,反馈循环因此很短。
- 标准的后端服务:REST 与 gRPC API、微服务、编排服务、后台任务。
- 需要快速推进、让新工程师迅速上手的团队。
- 高并发的网络服务,这正是 goroutine 大显身手、由运行时承担难点的地方。
- 凡是简洁与可读性比榨取最后几个百分点性能更重要的场景。
代价是 Go 带有垃圾回收。对绝大多数服务来说,这根本不是问题,它的回收器非常出色。但这意味着偶尔的停顿,以及对内存布局更少的控制,而这对某一类对延迟敏感的工作至关重要。
Rust 胜在哪里
Rust 提供 C 级别的性能,在编译期强制保证内存安全和线程安全,而且没有垃圾回收器。让新手头疼的借用检查器,正是让一整类错误(释放后使用、数据竞争、空指针解引用)根本无法被发布出去的机制。当正确性和速度都很重要时,这一保证值得你付出更陡峭的学习曲线。
- 系统与网络软件:协议、代理服务器、Agent、数据面。
- 无法容忍垃圾回收停顿的实时管线,比如实时音视频。
- 大型系统中对性能要求严苛的组件,通常通过 FFI 调用,或作为一个职责单一的独立服务。
- WebAssembly:Rust 在这里是一等公民,可以把原生速度的代码带进浏览器。
代价也实实在在:Rust 学起来更久,写起来更久,编译也更久。刚接触它的团队在最初几个月会慢一些。在合适的领域,这笔投入会有回报;在不合适的领域,它纯粹是额外开销。
我们实际如何选择
在我们自己的平台上,分工是自然形成的:迭代速度和可读性最重要的地方用 Go,时延和内存安全不容妥协的地方用 Rust。我们没有选边站,而是让工具匹配手头的任务。
当崩溃、数据竞争或回收器停顿确实不可接受时,选 Rust。其余几乎所有情况,选 Go。而大多数后端,恰恰属于“其余情况”。
一个实用的默认做法:除非有具体理由,否则新后端从 Go 开始,再有针对性地为真正需要的组件引入 Rust。你不必为整个系统只选一种语言。一个通过 FFI 调用 Rust 组件的 Go 服务,或是一个位于 Go API 之后的小型 Rust 服务,都是完全合理的架构。
如果你正在为一个真实项目权衡这个选择,想听听同时用这两种语言交付过产品的人的第二意见,这正是我们乐于展开的那种对话。