最近下载一个命令行工具时,我看到安装器同时提供了“Go版本”和“Rust版本”两个包。犹豫了三秒,我选了Rust。说不清为什么,可能是觉得“用Rust写的工具更靠谱”吧。但冷静下来想想,这个直觉本身就是一种时代偏见。十几年前,我拿到一个开源软件时,只会看它是不是用C写的,因为C代表性能;再早些年,大家会问是不是Perl写的,因为Perl代表灵活。语言选择这件事,从来不只是技术问题,它像一面镜子,照出我们脑子里的秩序感。
C语言是1972年诞生的,那是个程序员可以“徒手干仗”的年代。Dennis Ritchie给世界一个能直接摆弄内存的工具,没有GC,没有所有权,一切靠自觉。我年轻时写了个简单的网络服务,因为忽略了一个边界检查,半夜被线上报警惊醒。那一刻我突然理解了C语言的老话:它给你自由,也给你通往Segfault的直通车。C的哲学是“信任程序员”,但事实证明,人类在凌晨两点半的专注力并不值得信任。所以后来出现了Java,用一种近乎霸道的监管方式强制你规范,代价是让代码变得又长又啰嗦。
如果说C是荒漠上的自由人,那么Go更像是看透一切的退役老兵。2009年,Google推出了Go,它没有继承C++的复杂,反而背道而驰。goroutine比线程轻,channel比锁优雅,但代价是你得放弃泛型(直到1.18才补上)和异常处理。我写Go时感到一种奇特的放松——就像住在设施齐全的养老社区,规则明确,节奏慢,但偶尔想做点“出格”的事就会发现墙已经砌好了。Go社区反复强调“简单”,但这种简单在某种程度上是悬崖边上的退后一步:与其给你高级枪械,不如发你一把靠谱的刺刀。
然后Rust来了,带着Mozilla的血统,也带着一种近乎宗教化的自律。它不打算放弃性能,更不打算放弃安全。它用所有权、借用和生命周期在编译期就筑起一道防火墙,让你没法写出悬空指针。我第一次写Rust时,被编译器无情地拒绝了十几次,那种感觉像被一个严格的门卫反复盘问:你的这个引用有没有人比他更早离开?我甚至一度觉得愤怒,但后来慢慢理解了,这不是限制,而是一种更高阶的沟通——语言设计者在用自己的方式替你承担那些你根本看不见的风险。Rust的哲学不是“自由”,而是“通过规则来获取真正的自由”,这就很反直觉。
我常常觉得,每种流行的语言都承载着一代人的隐痛。C语言是技术英雄主义的余晖,Go是互联网公司流水线化需求的产物,而Rust则是对过去二十年安全漏洞频发的一种集体性的焦虑。我们骂C顽固,骂Go笨拙,骂Rust难学,其实是在用我们内心不同侧面的自己掐架。说到底,语言只是工具,真正的问题是:你想要机器替你扛多少责任?你又愿意牺牲多少灵活性去换取安稳?我至今仍在写C,但每次用malloc之后都会默默多写一行检查——那是Rust教会我的敬畏。语言会过时,但这种敬畏,大概能保存很久。