对 Go 和 Rust 的热爱吐槽!
Source: Dev.to
Warning: rant!
引言
我已经受够了这玩意儿。每隔几周,就会有 Rust 爱好者爬到这里,摆出一副自以为是的 “但你尝试过无畏并发吗?” 的说辞,装作 Rust 是编程语言的第二次降临,终于把世界上所有的问题都修复了。新闻快报:并不是。它是一个臃肿、过度设计的烂摊子,伪装成万灵药,而它的并发模型是现代编程语言讨论中被夸大得最离谱的谎言之一。
Go 的并发模型
Go(以及在它之前的 Erlang/BEAM)实际上把并发做对了。
- Goroutine 费用极低。
- Channel 是内置且符合惯用法的。
- 调度器 经受了实战考验。
你可以启动成千上万——甚至上百万——个并发任务,而不会让你的大脑崩溃,也不会让编译器大发脾气。它简单、可预测,并且可以在真实的生产系统中扩展,而不需要在生命周期注解上拥有博士学位。
Rust 的并发模型
Rust?哎呀。他们吹嘘 “无畏并发”,因为借用检查器在编译期捕获数据竞争。酷的故事,兄弟。但你猜怎么着?这只是一半的战斗——而且只是一小半。
真正的并发 bug 不仅仅是共享可变状态;还有死锁、活锁、饥饿、优先级反转,最糟糕的是:阻塞执行器。
在 Rust 主流的 async 生态(Tokio、async‑std、各种时髦的实现)中,一切都是在线程池上的协作式多任务。一个傻瓜调用了阻塞函数——.await 一个暗地里执行 std::thread::sleep、同步读取文件,或调用了内部阻塞的 crate——砰,你就把整个运行时饿死了。你的 “高度并发” 应用在一个线程上卡死,而其余线程闲置。
这和 Python 的 GIL 或 Java 的线程阻塞废话完全一样。Rust 并没有阻止它;它只是让你手动去追踪它,包裹在
spawn_blocking中,或者祈祷你的依赖库是彻底 async‑down 的(剧透:并不是)。
别再给我来那套 “但有 Send/Sync trait!” 的废话。Trait 并不会神奇地让第三方库变成非阻塞。只要有一个懒惰的开发者(或一个过时的 crate),你的无畏并发就和任何带 GC 的语言一样脆弱。
对比
| 方面 | Go | Rust |
|---|---|---|
| 并发原语 | Goroutine、内置 channel | Futures、async 运行时(Tokio、async‑std) |
| 阻塞陷阱 | 很难意外阻塞整个系统 | 只要一次同步调用就容易阻塞执行器 |
| 编译期安全 | 通过垃圾回收实现内存安全 | 借用检查器保证内存安全 |
| 开发者体验 | 编译快、单二进制、代码可读性高 | 学习曲线陡峭、到处都是生命周期、async 样板代码冗长 |
| 典型使用场景 | 服务器、后端、网络服务 | 低层系统,需要零成本抽象且不想有 GC 暂停的场景 |
Rust 的借用检查器对内存安全很有帮助(恭喜,你在没有 GC 的情况下重新发明了 GC),但代价是要不断与编译器搏斗、在每个签名里写生命周期,以及让 async 代码变得 “染色” 且具有传染性,把整个代码库变成投针噩梦。
结论
Go 能把事儿做好。编译快、单二进制部署、乏味却易读的代码让新人几天内上手,并且并发真的让人愉快。Rust?学习陡坡、借用错误让你怀疑人生,还有一群传教士认为冗长 = 美德。
Rust 适合它本来的定位:低层系统,需要零成本抽象且不想有 GC 暂停。但对于服务器、后端、网络服务——也就是我们在 r/golang 里干的活——它是被夸大包装的过度设计。别再这里推销你们的邪教语言了。我们有 goroutine。我们有 channel。我们有可扩展的简洁性。
Go 才是王者。Rust 只不过是后排那个大声喊 “但我的所有权模型!” 的孩子。
给我点踩吧,Rust 党。我会在这里继续交付代码,而你们还在和编译器搏斗。