成2023年最大技术分歧 Rust Go 还是 吵翻了!到底该选 (成2023年中国火箭发射全记录)

成2023年最大技术分歧 Rust Go 还是 吵翻了!到底该选 (成2023年中国火箭发射全记录)

2023 年,我们有一千个学习 Rust 的理由。

8 月 7 日,Rust 基金会发布了 2022 年度 Rust调查报告结果,报告显示 Rust 采用率不断提高,超过 90%的调查受访者表示自己是 Rust 用户;29.7% 的受访者表示,他们在工作中的大部分编码工作都使用 Rust,比上一年显着增加了 51.8%。

毋庸置疑,Rust 以其卓越的内存安全性和并发性能正日益成为开发者关注的焦点。然而,同样令人难以忽视的是 Go,这门曾被评选为年度编程语言的相对比较“老牌”的选手。

Go 语言诞生于 2009 年,一开始就因其独特的并发模型和强大的性能优势而受到了极大关注。值得注意的是,跟 Rust 语言一样,Go语言的创建者也同样“讨厌”C++,并且 Go 同样也都是云原生的主导语言。

而在 Stack Overflow 2022 开发者调查中,对于“让人爱恨交织的编程语言”这个问题,在 7 万份回复中,程序员们明显也更为偏爱 Rust,86%的人表示喜欢 Rust,而 64%的人表示喜欢 Go。面对 Rust 的火爆现状,一些开发者发出了灵魂提问:2023 年,Go 还值得学习吗?

另外,这两天,到底是该选 Rust 还是选 Go,也成为了 Hacker News 上的一个热门话题:

一位挺 Rust 的网友说道:“我也为这个选择烦恼了很久。最终 Rust 胜出了。首先,我感觉 Rust 更接近于以前 Pascal 时代的东西,你可以控制一切;其次,如果 wasm 和相关技术大爆发,Rust 将是一个更安全的选择;然后,我们已经有了 Python 用于快速开发,因此选择一些更极端的东西是有道理的,Go 在某种程度上处于中间地带。最后,Rust 应用于内核且备受关注,所以不太可能会被淘汰。”

另一位持反对意见的开发者则表示,“我从事 Go 开发已经快十年了,但最近我也尝试了下 Rust。我认为目前有一些对 Rust 的强制性和误导性偏好,从我在各种初创公司的经验,包括我目前所在的公司来看,对于后端开发来说,Go 是迄今为止最佳选择!注意,在性能、功能或其他方面……这两种语言非常非常相似!”

不得不说的是,Go 和 Rust 绝对都是优秀的编程语言。它们现代、强大、应用广泛,而且有着卓越的性能表现。但如果直接对比 Go 和 Rust 谁更好之类的,真的没啥意义,因为每种编程语言都代表着背后一系列深层次的权衡。不同的语言会针对不同的需求进行优化,因此我们在选择语言时,也应该考虑自己想要用它解决什么样的问题。所以我们将从 Go 和 Rust 语言的适用场景出发,探讨下 Go 与 Rust 的设计之“道”。

虽然 Rust 和 Go 在语法和风格上差别很大,但它们都是构建软件的一流工具。下面咱们开始具体分析。

Rust 和 Go 有很多共同点,所以人们才经常把二者拿来相提并论。那它们有哪些共同目标?

Go 和 Rust 都属于重视内存安全的现代编程语言。在 C 和 C++等旧语言发展的这几十年间,我们已经清楚地意识到,引发错误和 bug 的核心原因之一,就是对内存的不安全/不正确访问。

于是 Rust 和 Go 各自给出了不同的解决思路,但二者的目标都是在内存管理方面更智能、更安全,帮助开发者编写出正确且性能极佳的程序。

快速、紧凑的可执行文件

二者都属于编译语言,也就是说可以将程序直接翻译成可执行的机器代码,这样就能把程序部署成单一二进制文件。跟 Python 和 Ruby 等解释性语言不同,我们不需要随程序一同发布解释器和大量的库/依赖项。作为这个核心优势的直接体现,Rust 和 Go 程序的运行速度往往比解释性语言更快。

Rust 和 Go 都属于功能强大且可扩展的通用编程语言,大家可以用它们开发出各种现代软件——从 Web 应用程序到分布式微服务,还包括嵌入式微控制器和移动应用等等。

两者都拥有优秀的标准库和蓬勃发展的第三方生态系统,外加强大的商业支持与庞大的用户基础。二者已经存在多年,并将在未来几年继续保持旺盛的发展势头。如今,学习 Go 或者 Rust 将是非常合理的时间和精力投入方向。

两者既不过多偏向函数式语言(例如 Scala 或 Elixir),也不完全面向对象(例如 Java 和 C#)。相反,虽然 Go 和 Rust 都具备函数式及面向对象编程的功能,但却始终强调务实取向——即以最合适的方式解决问题,而不是通过“意识形态”强迫大家用特定的方法做事。

但如果您确实喜欢函数式编程风格,那 Rust 这边的相关工具选项更多,这也是 Rust 优于 Go 的一点。

Rust 和 Go 都为大规模编程提供不少有用功能,所以它们都能适应大开发团队作战和大体量代码库的现实需求。

例如,C 程序员多年来一直在争论应该把括号放在哪里,还有代码要不要用制表符或空格进行缩进;但 Rust 和 Go 早已使用标准格式化工具(Go 有 gofmt,Rust 则是 rustfmt)彻底解决了这些问题。它们会使用符合规范的风格自动重写你的代码。

并不是说这种特定的格式有多精妙,而是 Rust 和 Go 程序员更加务实、宁愿选择统一的执行标准。

这两种语言的另一大优势,体现在构建管线上。二者都有优秀、内置且性能出色的标准构建与依赖项管理工具。就是说程序员不必跟复杂的第三方构建系统对抗,也用不着每隔几年就学习一种新系统。

聊了这么多问题,再加上两种语言都设计得如此精良、功能如此强大,那这场比拼到底有没有结果?或者说,既然二者都是非常出色的选项,那为什么人们还会在社交媒体上出离愤怒,撰写长篇累牍的评论博文放出“白痴才用 Rust”或者“Go 根本不能算编程语言”之类的狠话?

有些人当然只是为了宣泄情绪,但这显然无助于解决实际问题。至少在项目中该用哪种语言、或者该靠哪种语言闯荡编程世界这种事上,嗓门大显然无助于做出正确选择。

下面咱们回到成年人的讨论,看看理性分析之下 Rust 和 Go 之间如何互有长短。

之前已经提到,Go 和 Rust 生成的程序运行速度都很快,因为它们会被编译成本机机器码,无需通过解释器或虚拟机这个步骤。

但 Rust 的性能还是要更胜一筹,甚至能够与被称为业界性能标杆的 C 和 C++相媲美。而且跟这些老牌语言不同的是,Rust 还提供内存安全与并发安全机制,同时几乎不影响执行速度。Rust 还允许开发者构建复杂抽象,又无需在运行时承受性能损失。

相比之下,虽然 Go 程序的性能也不错,但其设计重心主要在于开发速度(包括编译)、而非执行程度。Go 程序员更倾向于代码的清晰可读,所以运行速度要稍逊几分。

Go 编译器也不会花费太多时间来生成最高效的机器码,它更关心如何快速编译大量代码。所以在运行时基准测试中,往往是 Rust 程序要压 Go 程序一头。

Rust 的运行时性能还具有良好的一致性和可预测性,因为它没有使用垃圾收集。Go 的垃圾收集器非常高效,而且做了优化以尽可能缩短暂停时长(随着 Go 新版本的发布,暂停时长也是越来越短)。但无论如何,垃圾收集总会给程序的行为方式带来一些不可预测性,而这对某些特定应用(比如嵌入式系统)而言可能很严重、甚至完全不可接受。

因为 Rust 的目标是让程序员完全控制底层硬件,所以 Rust 程序都能深度优化以接近机器的最大理论性能。如此一来,Rust 就在执行速度胜过其他一切的特定应用场景下成为最佳选项,此类用例包括游戏编程、操作系统内核、网络浏览器组件和实时控制系统等。

如果一种编程语言过于难学、把大多数人都挡在了门外,那它的性能再强也没有意义。Go 在设计上似乎就是刻意要跟 C++等复杂度不断提升的语言区分开来:它语法极少,关键字也极少,就连功能都不多。

这意味着 Go 语言很容易上手,稍微了解之后就能用它编写出各种程序。

这里的重点就是“简单性”三个字。当然,简单并不代表容易。可一门小而简单的语言,学起来肯定要比大而复杂的语言要轻松。实现一种效果的方法并不多,所以高质量的 Go 代码看起来几乎都是一个样。这还带来另一个好处:我们可以快速理解某项自己不熟悉的服务到底在做什么。

fmt.Println("Gopher's Diner Breakfast Menu")for dish, price := range menu {fmt.Println(dish, price)
复制代码

Go 的核心本体虽然很小,但标准库却非常强大。也就是说,除了 Go 语法之外,我们的学习曲线还必须考虑到标准库这个部分。

另一方面,把功能从语言转移到标准库,意味着大家只需要专注学习跟当前开发需求相关的库。

Go 在设计上也充分考虑到大规模软件开发需求,能够有力支持大型代码库和开发团队。在这类场景下,新加入的开发者必须能够快速上手。为此,Go 社区一直将程序的简单、明确、通用和直接放在首位。

Rust 经过专门设计,包含多种强大且有用的功能,可以帮助程序员用最少的代码完成更多任务。例如,Rust 的匹配功能就可快速编写出灵活且富有表达力的逻辑:

fn is_prime(n: u64) -> bool {0...1 => false,_ => !(2..n).any(|d| n % d == 0),
复制代码

但也因为 Rust 在设计上考虑得多,所以学起来也就更困难一点,特别是在起步阶段。但没关系,毕竟 C++或者 Java 也有很多东西要学,甚至还无法提供内存安全之类的 Rust 高级功能。

所以批评 Rust 太过复杂的声音实在没啥道理:它在设计上就是在强调表达能力和丰富的功能,我们不可能在占了好处的同时又指望着它能那么简单纯粹。

所以 Rust 当然有自己的学习曲线。但只要跨过了这道难关,后面就是一马平川了。

Rust 和 Go 之间虽然彼此借鉴了一些功能(比如说泛型),但公平地讲,Rust 的功能还是更胜一筹,Go 的功能相对要匮乏一点。

大多数语言都为并发编程(即同时执行多项操作)提供某种形式的支持,但 Go 则是从头开始就为此而设计。Go 不使用操作系统线程,而是提供一种轻量化的替代方案:goroutines。

每个 goroutine 都是个独立执行的 Go 函数,Go 调度程序会将其映射至控制下的操作系统线程之一。也就是说,调度程序可以非常高效地管理大量并发 goroutine,且只须使用有限数量的操作系统线程。

因此,我们可以在单个程序中运行数百万个并发 goroutine,又不必担心引发严重的性能问题。正因为如此,Go 成为 Web 服务器和微服务等大规模并发应用场景下的完全解决方案。

Go 还为 goroutines 提供 channels,这是一种快速、安全、高效实现数据通信和共享的方法。Go 的并发设计水平确实很高,使用体验也相当轻松愉快。

一般来说,并发程序的设计难度很大,在任何语言中构建起可靠且正确的并发程序都绝非易事。但由于在立项之初就考虑到这方面需求,所以 Go 中的并发编程机制已经做得尽可能简单且得到良好整合。

相比之下,Rust 中的并发机制刚刚落地、还没有最终稳定,所以欢迎大家继续关注这个活跃的开发方向。这样也有好处,比如 Rust 的 rayon 库就提供一种非常优雅且轻量级的方法,能够将顺序计算转换为并行计算。

虽然在 Rust 中实现并发程序可能不太容易,但仍然完全可行,而且这些程序还能获得 Rust 精心设计的内存安全保障。

以标准库的 Mutex 类为例:在 Go 当中,我们可能会在访问某些内容前忘记获取互斥锁;但在 Rust 这边则完全不需要担心。

前文已经提到,Go 和 Rust 都会以各自的方式防止各种常见的编程错误,特别是跟内存管理相关的问题。但 Rust 走得更远,可以说是不遗余力地保证大家不致搞出意料之外的安全纰漏。

也就是说,Rust 的编程体验跟几乎所有其他语言都有所不同,而且在刚刚接触时可能相当具有挑战。但在不少开发者看来,这份付出显然物有所值。

包括 Go 在内,很多语言也提供帮助程序员避免错误的工具,但 Rust 把这种效果提升到了新的水平。很多不正确的程序甚至根本没办法编译。

“与借用检查器作斗争”是 Rust 新人们必须要过的一关,但在大多数情况下,检查器并不是真正的敌人。它发现的问题确实是代码中的真实 bug(或者至少是潜在 bug)。它可能迫使我们从根本上重构自己的程序来避免此类问题——如果各位确实把正确性和可靠性当作首要任务,那这种严格要求显然是件好事。

换个角度想,不改变编程方式的新语言,能叫新语言吗?而且在使用其他语言时,Rust 教会我们的安全思维同样意义重大。

上手 Rust 编程模型的实际难度,可能取决于大家之前用过哪些其他语言。Python 或者 Ruby 程序员可能觉得 Rust 限制太多,但其他人可能觉得这种清晰明确的约束也不错。

当大家独自或者在小团队中解决问题时,要选简单的语言还是丰富的语言纯属个人喜好。但随着软件规模的扩大、复杂度的提升、团队的膨胀,两类语言之间的差异才开始真正显现出来。

对于大型应用程序和分布式系统,代码执行速度的重要性往往低于开发速度:像 Go 这种刻意强调精简设计的语言能够缩短开发新手的适应时间,也让他们能更快参与到大型代码库的贡献当中。

在涉及大规模软件开发时,明确易读总是比精巧优雅更重要。Go 的局限性实际使其比 Rust 等更复杂、更强大的语言,要更适应企业和大型组织的需求。

虽然 Rust 和 Go 都是高人气且得到广泛应用的现代语言,但二者间并不是真正的竞争对手,因为它们所面向的用例可以说完全不同。

Go 的整个编程方法就跟 Rust 完全不同,这些特性一方面特别适合某些人,但另一方面也会彻底激怒某些人。这很正常,因为如果 Rust 和 Go 都在以基本相似的方式解决基本相同的问题,那我们干嘛还需要两种独立的语言?

那么,我们能不能从 Rust 和 Go 采取的方法入手,解读它们各自的本质呢?下面就一起来试试。

“要垃圾收集,还是不要垃圾收集”永远是个没有正确答案的问题。一般来说,垃圾收集和自动内存管理能帮助我们快速、轻松地开发出可靠且高效的程序。所以对某些开发者来说,这些都是必不可少的功能。

但也有人认为,垃圾收集和它带来的性能开销与全局暂停,会导致程序在运行时的行为变得不可预测,同时引入不可接受的延迟。这话当然也有道理。

计算机编程的发展史,可以说是一段日益复杂的抽象发展历程。它让程序员们既能解决问题,又不用太多关注底层硬件的实际运行方式。

这种设计让程序更易于编写、更具可移植性。但对于其他一些程序来说,访问硬件及精确控制程序的执行方式反而更加重要。

Rust 的目标就是让程序员能“贴近硬件”,夺回更多控制权;而 Go 则抽象掉了架构细节,让程序员更贴近问题。

其实对大多数程序来说,性能的重要性是不及代码可读性的。但如果某些项目确实是以性能为先,那 Rust 中的很多设计权衡,将帮助大家把代码的执行速度一路推向极限。

相比之下,Go 更关心代码简单性,甚至愿意为此牺牲一些运行时性能。但 Go 的构建速度无与伦比,这对大规模代码项目来说往往更加重要。

另一方面,如果不强求程序永不出错,那取舍以会不同。大多数代码都不会考虑到长期使用,但某些程序也确实在生产环境中多年运行。

面对这些现实情况,也许我们有必要投入一点额外的时间,来开发并保证程序能够正确、可靠运行,且未来不致引发沉重的维护负担。

Go 和 Rust 都能帮助大家编写出正确的程序,只是具体方式各有不同:Go 提供出色的内置测试框架,而 Rust 则专注通过借用检查器消除运行时 bug。

虽然 Go 和 Rust 都足以支撑起严肃的开发项目,但大家最好还是能充分了解它们的各种特性和优势。

总之,别人的想法并不重要:只有你自己能决定哪种编程语言更适合你的团队及项目需求。

希望这篇文章能帮助大家理解 Rust 和 Go 的各自亮点。如果可能的话,各位最好能多少体验一下这两种语言,因为它们在任何技术道路上都非常有用,哪怕对业余编程爱好者也是如此。

但如果您的时间只搞认真钻研一门语言,那请千万先把 Go 和 Rust 各自的专长和倾向性搞清楚,之后再做选择。

当然,关于编程语言的知识只是成就一名成功软件工程师的很少一部分。除了编程之外,工程师们还得精通设计、工程、架构、沟通和协作。只要大家能把后面这几样做好,那无论你选择哪种编程语言,都将成为一名出色的软件工程大牛。

声明:本文来自用户分享和网络收集,仅供学习与参考,测试请备份。