为什么是 Go 而不是 Rust

导读: Go 和 Rust 都是时下非常火爆的语言,这两者孰优孰劣,在网上就形成了两大阵营,互不相让。小编翻译本文后不久就赫然发现一篇与本文“正面刚”的文章: Why Rust and not Go ,小编大致看了一下,这两篇文章互相批驳彼此,有兴趣的读者看完本文后,不妨也看看那篇反驳的文章。

在 Rust 的宇宙中,Go 扮演的角色是什么?

假设你是一名主要用 Go 语言的开发者。你去参加一次活动,在和一些人聊天的时候,你决定告诉他们,你为了做某事而编写了一个小工具。你声称,这个小工具是用 Go 语言编写的,因此速度相当快,它是一个二进制文件,等等。大家似乎对你的叙述感到很满意,你开始自我感觉良好,但随后,你注意到,有一个陌生人从后面走过来,一个声音传来,句句渗透着道道寒意:“你为什么用 Go 而不用 Rust 呢?”

你开始感到有些局促不安。好吧,你可以回答你只知道 Go 语言,所以就用 Go 来解决问题,但这可能不会是一个令人满意的答案。你在一开始就自豪地炫耀你的工具跑得有多快,但很显然,陌生人会用 Rust 给 Go 带来的好处来反驳你过于简单化的借口。

我开始感到很沮丧。当初你为什么会选择学习 Go 语言?有人告诉你,Go 的速度很快,而且它有很好的并发原语。现在,Rust 横空出世,每个人都在说,Rust 在各个方面都更好。他们是以前就说谎了呢,还是现在就在说谎呢?虽然没有单一的语言能够统治所有的语言,但你知道,人们仍然有可能做出错误的选择,最终陷入技术死胡同。毕竟,几年前你确实在其他语言中,选择了 Go 语言,你很高兴地加入圈子并融入其中,还问“为什么不用 Go 呢?”

虽然上面的故事完全是我虚构出来的,但毫无疑问的是,Rust 有一些拥趸,他们过于激进,觉得有义务向每一个迷失的灵魂灌输螃蟹大神的美德。(译注:Rust 语言的吉祥物就是一只螃蟹 Ferris,这是因为 Rust 开发者有一个名字,叫 Rustacean,因为这个是从甲壳纲动物这个单词 Crustacean [krʌ’steʃən],去掉了首字母 C,而演变而来的。因为这里面包含 Rust 这四个字母。)这真的并不是 Rust 的错,每个成功的项目都会有行为不端的追随者,这是很难避免的。虽然每个人都必须与这些人打交道,但我觉得,Go 开发者特别容易受到他们行为的影响,因为 Rust 和 Go 的消息传递有太多的重叠。

Go 速度很快,但 Rust 速度更快。

Go 有一个高效的垃圾收集器,但 Rust 有静态内存管理。

Go 有很好的并发支持,但 Rust 有可证明的正确并发性。

Go 有接口,但 Rust 有特性和其他零成本的抽象。

如果你是一名 Go 开发者,你可能会有点上当受骗的感觉。相比之下,Python 开发者就不怎么特别担心 Rust。他们知道,Python 在很多方面,速度慢,效率低,但他们对此并不介意,因为他们知晓 Python 的角色:使代码易于编写,并在当性能很重要时,将其转到 C 语言来开发。

Go 语言怎么样呢?

Go 非常适合用来编写服务

Google 创建 Go 语言是为了解决 Google 的问题,这些问题主要涉及网络服务。Go 的并发模型非常适合服务器端应用程序,这些应用程序必须主要处理多个独立请求,而不是参与复杂的结果传递方案。这就是为什么给你的是 go 而不是 wait 的原因之一。

Go 对 HTTP 和相关协议有很好的支持,并且,编写一个令人满意的 Web 服务并不需要很长的时间。在我的个人项目中,Go 被证明是 Node.js 的很好替代方案,尤其是在我想要比编写惯用的 JavaScript 更明确地确定不同组件之间的接口的情况下。

除此之外,它还有强大的工具,可用来诊断并发性和性能问题,而且交叉编译使得 Go 在任何平台上部署都变得轻而易举。

Go 无可置辩地简单

Go 以提供了一组有限的内置语言功能而感到自豪。这使得 Go 易于学习,更重要的是,它确保了 Go 项目即使在规模不断扩大的情况下,代码仍然可以理解。Go 的创造者喜欢称之为一种“无聊”的语言。虽然我们可以争论这种语言是否可以使用一种或两种额外的东西,但事实证明,迫使人们“少花钱多办事”的想法是非常成功的。

在 Web 服务方面,Rust 确实可以做到和 Go 一样好,甚至更好,但就简单性方面而言,它确实不能与 Go 相比。而且 Go 不只是简单,它对其他语言通常比较宽松的东西也很严格。Go 不希望在同一个目录中有未使用的变量或导入、属于不同包的文件等等。它甚至曾经抱怨在 GOPATH 之外保存的项目(谢天谢地,现在不再是这样了)。

Go 也不希望在代码中留有任何“指纹”,因此它通过 go fmt 强制转换成单一的、通用的代码风格。

事实上,这些事情没有一件特别令人印象深刻,但它们确实描述了 Go 语言想要强加于人的心态。因此许多人不喜欢它。但在我看来,它是某些开发类型(如企业软件)的杀手级特性。

Go 对企业软件来说非常棒

正如我已经提到的,Go 的创建,是为了解决 Google 的问题,而 Google 的问题绝对是企业级的问题。无论这是创造者的直接目的,还是在大公司使用它的自然结果,Go 宛如一股清流,无疑为企业软件开发带来了令人兴叹的新鲜空气。

如果你有编写企业软件的经验,并且尝试过 Go 语言,那你可能会明白我的意思。下面是简短的总结。

与其他类型的开发相比,企业开发是一个非常怪异的“野兽”。如果你从来没有这样做过,你可能会扪心自问:“到底什么是企业软件?”我曾经做过一段时间的开发顾问,下面是我对这一问题的看法。

企业软件开发与规模有关

企业软件并不以用户总数或数据量为依据。通常情况亦如此,但定义性特征是范围过程的规模。

企业软件总是有很大的范围。 这个领域可以大而宽,也可以是窄而复杂,有时二者兼而有之。当创建软件来为这样的领域进行建模时,由于非技术方面的关注超过了大多数技术方面的考虑,造成正常的编程智慧极其短缺。

要解开这一复杂的领域,你需要一个结构良好的过程。 你需要领域专家、分析师和一种机制,让利益相关者评估正在进行的流程。这也是经常发生的情况,作为技术专家,你并不十分了解这个领域。利益相关者和领域专家通常也不太了解技术。

这进一步降低了诸如效率、甚至正确性等技术问题。不要误会我的意思,企业确实很关心正确性,但他们对正确性有不同的定义。当你考虑算法的正确性时,而他们考虑的却是在劳动力廉价的国家为他们的运营团队建立一个协调的后勤办公室。

由于这个大前提所带来的环境,随着时间的推移,出现了一些著名的企业开发“怪癖”。我将举出三个与我的观点相关的例子。

  1. 有很多初级开发者在工作中学习如何编程,但大多数人并没有幸运地找到一份能够真正教会他们任何东西的工作。在一些地方,当你被聘用后,就会被安排在 PluralSight 进行为期一周的培训,然后你就被认为已经为上岗做好了准备。
  2. 出于各种错误的原因,软件项目很快变得庞大而复杂。 大型项目需要时间来构建,而人员(或整个团队)会在此期间流动。Constant 重构从来都不是一种选择,因此每个重构都会留下大量代码,这些代码的质量参差不齐。多个团队并行工作也会产生冗余代码。领域会随着时间的推移而发生变化,不可避免地使旧的假设失效,从而引起抽象泄漏。抽象越复杂,当业务返回严重的变更请求时,它成为问题的风险就越大。
  3. 工具链通常很槽糕,而且经常过时。 到目前为止,这几乎是我所描述的一切的必然结果。大量的旧代码会将你束缚在特定的工具集上,初级开发者充其量只能了解现状,而高层(管理者和利益相关者)往往根本就没有准备好根据第一手经验做出技术决策,这种努力的一般性质使他们厌恶风险,导致他们主要以模仿自己所在领域的其他成功玩家所做的事情为主,或者更确切地说,分析师声称,其他成功的玩家都是这样做的。

Go 就是要在规模上抑制复杂性

Go 让团队更成功,部分原因是基于他们比其他生态系统更多的东西,部分原因是拿走他们的工具,来防止掉进常见的陷阱。

Go 比 Java 或 C 语言更容易学习。加快速度通常是好事儿,但当项目落后、截止日期临近时,管理层必然诉诸于雇佣更多的人,希望(徒劳地)加快项目进度时,加快速度就变得至关重要了。

Go 社区将 Java/C 等经常使用的许多抽象视为反模式,例如 IoC 容器,或 OOP 继承等等。只有两个级别的可见性变量,而且唯一的并发模型是 CSP。使用 Go 进行编程,与 Java/C 相比,Go 更难陷入难以理解的陷阱。

Go 编译器速度很快。 这意味着,在通常情况下,运行测试会更快,部署会话费更少的时间,从而提高了整体生产率。

使用 Go,初级开发者更容易提高工作效率;而作为中级开发者,更难引入脆弱的抽象概念,这些抽象会导致出现问题。

处于这些原因,对于企业软件开发来说,Rust 不如 Go 那般有吸引力。但这并不意味着 Go 就是完美的,也并不意味着 Rust 比 Go 更有优势。相比 Go,Rust 固然有很多优点,但最重要的是,我认为,人们对 Go 的普遍看法是错的。

Go 谈不上极快,而且内存利用效率也不是超强。Go 也没有绝对最好的并发模型。

Go 比 Java/C# 更快、更有内存效率,并且绝对比 Java/C# 有更好的并发性。Go 很简单,所以,当面对普通的 Go 程序与普通的 Java/C# 程序时,所有这一切都可以成立。 从绝对意义上来说,GO 是否真的比 C# 或 Java 快,这并不重要。普通的 Java/C# 程序户与最好的理论程序大相径庭,并且与 Go 相比,这些语言中的危险东西太多了。如果你想要一个例子,看看这篇 Brandon Minnick 的关于 C# 并发性的演讲《纠正 .NET 常见的异步 / 等待错误》( Correcting Common Async/Await Mistakes in .NET),在我看来,这是难以置信的,因为直接使用 await ,从来都不是正确的做法。试想一下,普通的异步 C# 应用程序有多糟糕。事实上, ASP.NET 应用程序出现死锁时,却没有明显的原因,这样的情况并不少见。

图0:为什么是 Go 而不是 Rust

它会发生死锁吗?
上面图片取自这篇博文:《理解异步,避免 C#中的死锁》(Understanding Async, Avoiding Deadlocks in C#),该文试图解释了 C# 中分解并发的无数种方式。

结论

Go 是一个更好的 Java/C#,而 Rust 则不是。Go 可以给企业软件开发带来清晰度,这无疑比降低整体生产率为代价的清除垃圾收集更有价值。

Rust 是一个更好的 C++,即使你偶尔听到有人说 Go 是一个更好的 C,但事实并非如此。任何带有内置垃圾收集器和运行时的语言,都不能被视为 C 语言。别搞错了,Rust 才是 C++,而不是 C。如果你想要更好的 C,请看看 Zig。

最后,回到正题,并不是所有的“为什么不是 Rust”的问题都应该如上面的例子那样解释。有时候你会感到阵阵寒意,那些问你这个可怕问题的人,只想知道你的想法。我们要做的是避免将自己的身份与单一的语言捆绑在一起,首先要拥抱实用性。像 Rustacean 或 Gopher 这样的部落名字应该避免,因为它们本质上就是一种营销工具,用来诱导更强的品牌效应。

作者介绍:

Loris Cro 是供职于 Redis Lab 的开发者,爱好编程和设计原则。

本文文字及图片出自 InfoQ

余下全文(1/3)
分享这篇文章:

请关注我们:

共有 1 条讨论

  1. sunny 对这篇文章的反应是赞一个

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注