标签 TIOBE 下的文章

Gopher的Rust第一课:Rust的那些事儿

本文永久链接 – https://tonybai.com/2024/04/22/gopher-rust-first-lesson-all-about-rust

要说这两年后端编程语言谁最火,Rust说自己第二,没人敢说第一。Rust连续8年霸榜stackoverflow最受推崇的编程语言,甚至被推特之父Jack Dorsey称为“完美的编程语言”:

注:最火:仅代表人气最旺,最受欢迎,但并不代表使用者最多。

如果你经常读我的博客,你可能会问:“你不是Go语言布道师吗?怎么也要转Rust了?”其实不然,学习Rust不是要蹭热度,而是实际开发的需要。这些年在汽车行业这个赛道上,云端和车端都要兼顾。目前车端基础软件的开发语言主要是C/C++,但内存安全、性能不输C且高可靠的Rust日益受到车载软件开发的青睐,AUTOSAR组织在2022年成立了Rust工作组就是一个重要的信号。并且据我所知,一些国内造车新势力已经或正在将一些Rust开发的中间件或应用放到了量产车或即将量产的车上。

注:AUTOSAR (Automotive Open System Architecture) 是一个面向汽车电子系统的开放式软件架构标准,由汽车制造商、零部件供应商和电子供应商共同发起并持续维护的一个全球性标准化组织。

不过,Rust语言在某些领域的崛起确实引发了其他编程语言社区的一些不满和争议。特别是Rust社区的一些人提出“Rewrite Everything in Rust”的观点,让很多编程语言社区,尤其是C++社区十分不安。Go社区则相对更加开放和友好的,主流观点是Go与Rust是可以互补的,两种语言在各自的优势领域发挥作用,通过合作而非对抗的方式,能为开发者提供更好的选择。更多细节,可以参考几年前我曾翻译过的前Go团队产品经理、gohugo的作者Steve Francia联合创作的一篇文章《Rust vs. Go:为什么强强联合会更好》。

也就是说Go依然是我的主力语言,但考虑工作上的需要,我要系统学学Rust了。为了避免“从入门到放弃”,我打算采用边学习边输出的方式,一方面可以督促自己学习,另一方面也希望能和读者及时互动,纠正学习中的错误理解。

我的Go语言第一课专栏广受欢迎,其知识结构想必也是大家认可的,这里我就仿照其形式,写一下学习Rust的第一课这个入门系列。

正如我在Go语言第一课专栏所说的那样:我一直认为,当你开始接触一门新语言的时候,你一定要去了解它的历史和现状。因为这样,你才能建立起对这门语言的整体认知,了解它未来的走向。而且,也能建立起学习的“安全感”,即相信它能够给你带来足够的价值和收益,更加坚定地学习下去。

在这篇文章中,我就先来了解一下Rust的诞生历史和现状发展,以及它独特的设计哲学。并与Go语言做个简单对比,希望能够让自己和读者对Rust有一个初步的认识。

1.1 Rust的历史与现状

1.1.1 Rust的诞生与演进

Rust诞生于2006年,这比Google三巨头“密谋”创建Go语言还要早上一年。不过和Go的三位创始人:图灵奖获得者、C语法联合发明人、Unix之父肯·汤普森(Ken Thompson),Plan 9操作系统领导者、UTF-8编码的最初设计者罗伯·派克(Rob Pike),以及Java的HotSpot虚拟机和Chrome浏览器的JavaScript V8引擎的设计者之一罗伯特·格瑞史莫(Robert Griesemer)相比,Rust之父格雷登·霍尔(Graydon Hoare)的身份和江湖地位却没有那么“显赫”。彼时的他只是Mozilla Research的一位加拿大籍的、不到30岁的开发人员:

注:Graydon Hoare这个人非常低调,极少在公开场合露面,因此在网络上很难找到他的肖像,上面图中的肖像来自https://www.crunchbase.com/person/graydon-hoare,我这里甚至不能保证这个肖像就是Hoare本人的。

新生代编程语言的诞生都伴随着一段轶事,比如Go语言的创始人们在Google内部经常遇到C++项目漫长的编译时间问题,每当他们启动一个C++项目的编译,都要等很长时间,期间都能喝上几杯咖啡。这让他们深有感触并意识到需要设计一门编译速度更快的新语言,于是Go语言就这样诞生了。和Go语言“喝咖啡,等C++项目编译”类似,Rust的诞生也有一段轶事:

2006年,29岁的Hoare有一天回到温哥华的家中,但他发现电梯坏了,电梯软件崩溃了!他不得不爬楼梯回到位于21层的家中。当他爬上楼梯时,他感到很恼火。他想:“我们做计算机编程的人居然无法制造出一部可以正常工作而不崩溃的电梯!” Hoare知道,许多此类崩溃都是由于程序使用内存的问题造成的。电梯等设备内部的软件通常是用C++或C语言编写的,这些语言以允许程序员编写运行速度非常快且相当紧凑的代码而闻名。问题是这些语言也很容易意外引入内存错误,这些错误会导致崩溃。Hoare决定对此做点什么。于是他打开笔记本电脑,开始设计一种新的计算机语言,他希望这种语言能够编写小而快速的代码,而不会出现内存错误,他将其命名为Rust

这段轶事显然不可考证了。但可以确定的是从2006年开始的若干年里,Hoare创建的个人语言项目Rust并没有真正的用于改善电梯系统的程序,而是在得到了Mozilla的赞助下,用在了持续开发Mozilla的浏览器引擎Servo上了,Mozilla在2010年官宣了该项目,Hoare在2010年的一次演讲中也第一次介绍了Rust语言:

Rust开源的第一行代码也是在2010年完成的:

此外,最初的Rust编译器是由OCaml实现的,2011年Rust团队使用Rust基于LLVM重新实现了编译器并实现了自举。同年,Rust也有了自己的LOGO,其设计灵感来自于自行车齿盘

2012年,Graydon Hoare接受InfoQ专访,谈及他带领Rust team在Mozilla开发的系统编程语言Rust,包括Rust的特性、Rust相对于C/C++/Java/Go的优势与不同以及Rust的1.0版本发布计划。

但是,就在下一年,即2013年,Graydon Hoare就因为精力耗尽而辞去了Rust team的领导职务,离开了自己的Rust team,并从此远离了Rust开发。Hoare的离开对Rust team和语言本身来说是一次重大的损失,但Rust社区和团队采取了积极的措施来确保Rust的持续发展和演进。

2014年11月,Rust官宣了cargo和crates.io,前者是Rust项目构建管理器,后者则是Rust官方维护的Rust代码的中央包存储库,通过cargo可以轻松构建和发布包到crates.io,或从crates.io上拉取Rust代码的依赖。

2015年5月15日,Rust迎来了一个里程碑的时刻:Rust 1.0正式发布! ,这要比Go发布1.0版本迟了3年。但正如官博所说:“1.0版本的发布标志着混乱的结束。此版本是我们对稳定性承诺的正式开始,因此它为构建应用程序和库提供了坚实的基础。从现在开始,重大更改基本上超出了范围(一些小的警告适用,例如编译器错误)”。

Rust 1.0发布后,Rust的版本发布周期与节奏也得以确定下来,即每6周发布一个稳定版本,按照这个节奏,与Rust 1.0同时发布的还有Rust 1.1 Beta版本。经过六周的测试后,Rust 1.1 Beta转为Rust 1.1稳定版本,同时发布Rust 1.2 Beta版本,依次类推。当然,Rust还有一个nightly build版本,这个版本包含了最新但不稳定的特性。和Go社区和开发人员每年只能high两次相比,Rust开发者和社区更加幸运,每六周就能high一次!

Rust的演进是基于RFC(Request For Comments)驱动的,并且这一措施是早于Rust 1.0发布前就基于RFC确定下来的。这与Go的Proposal process类似,但感觉比Go的流程更规范和严谨,当然这与两种语言的治理结构的组成和规则有关。

然而,Rust 1.0的发布只是Rust语言发展的一个新起点,这件事并没有像Go语言在2009年宣布开源那样获得足够的曝光度并赢得TIOBE年度最佳编程语言的称号。

Rust之后的发展依旧是一波三折,这主要也是缘于Rust当时没有一个“好爹”:


TIOBE Rust曲线(2012~2024.3)

2020年,Rust语言迎来了自己诞生以来的至暗时刻。因新冠疫情全球流行导致的业绩下滑,2020年8月,Mozilla解雇了全球1000名员工中的250名,这其中就包含Servo引擎背后的团队。该事件引起了人们对Rust未来的担忧,因为团队的一些成员是Rust的主要贡献者。

但塞翁失马焉知非福,2021年2月8日,由五家创始公司(AWS、华为、谷歌、微软和Mozilla)共同赞助的独立非营利组织Rust基金会宣布成立!Rust团队终于有了新家,并且这次除了亲爹Mozilla外,还有四个财大气粗、执IT牛耳的干爹,Rust语言的未来一下变得光明了。

实际上Rust的发展也是如此,从2021年基金会成立至今(2024.4),Rust取得了长足的发展:语言特性不断增强,编译器性能持续优化,生态系统日渐壮大和完善,增加和完善了对WebAssembly、嵌入式、大数据、区块链、人工智能等领域的支持。下面我们就来说说Rust语言的现状。

1.1.2 Rust的现状发展

1.1.2.1 语言排名

虽然Rust热度很高,但在语言排名方面与几乎同期的Go还有一定差距,在2024.3月的TIOBE排名中,Go稳居第8位,而Rust虽然刷新了自己的历史最高排名,但也仅仅排在第17位:


TIOBE Rust 2024.3排名

Redmonk 2024.1月排名中,Rust位列19位,Go位列12位:


Redmonk Rust 2024.1排名

不过,Rust的热度和社区活跃度甚至要高于Go,究其根源,我认为还是与两个开源语言的治理结构有关,下面是Go和Rust在Reddit论坛上的拥趸数量与在线人数对比(2024.4.6 21:39北京时间):

如果能持续保持住这样的热度和发展势头,Rust可能在未来几年迅速接近Go的位置,甚至超越也是有很大可能的。

和Go开发人员自称Gopher类似,Rust开发人员自称Rustacean,这是一个结合了“Rust”和“Crustacean”(甲壳类)两个词语的组合词。此外,Rust社区还设计了Rust的非官方吉祥物(mascot):Ferris,一只可爱的红色螃蟹,它是由设计师Karen Rustad Tölva在2010年创作的。Ferris象征着Rust语言的安全性、并发性和生产力,同时也代表着Rust社区的活跃和友好。

crates.io上还有一个名为ferris-says的crate,可以用来打印Ferris吉祥物相关的文字,可以输出像下面这样的ASCII字符拼接出的Ferris形象:

1.1.2.2 语言采纳

从上面TIOBE的Rust排名曲线来看,Rust在2018 edition和2021 edition前后到达过两个“尖峰”。各大公司以及初创也基本都是在2018 edition之后开始逐渐采纳Rust的。

注:关于Rust edition,感兴趣的读者可以先参考Rust官方文档,在后续学习cargo和Rust项目编译构建的时候,我们还会深入学习和理解edition。

接下来,我们列举一下Rust基金会创始公司以及其他一些知名IT公司和组织对Rust的采纳情况。

  • AWS

除了成为Rust基金会创始成员,让大家真正知道了AWS对Rust投入的决心外,真正让大家看到AWS内部大量使用Rust的文章是2022年2月AWS在官博发表的一篇名为Sustainability with Rust的文章,这篇文章介绍了Rust在AWS内部基础设施构建上发挥的关键作用,包括用Rust进行Firecracker、AWS Lambda、Amazon S3、 Bottlerocket等开发。这篇文章还引用了一篇2017年发表的论文Energy Efficiency across Programming Languages中的结论,认为Rust在能耗方面的优势是其他语言如Go、Java不能匹敌的,这一定程度上引发了争议,记得Russ Cox在Twitter上海批驳了这篇文章中引用的数据不准确。

  • 华为

作为国内以一己之力力抗美帝的通信、IT、手机、汽车等多赛道公司,同样也是拥有处理器、OS、编译器等全技术栈的研发型公司,华为对Rust这一的系统级编程语言尤为青睐。但从公开资料上能看到的东西不多,从华为可信编程实验室的主页上,我们看到了Rust在华为应用的一些情况。

华为的目标是在全球最大的电信行业设计值得信赖的软件系统。华为正在努力将部分代码库迁移到Rust,它比C/C++更安全且性能更高。为了帮助开发人员完成这个过程,华为利用开源C2Rust翻译器直接从C生成Rust代码。

huawei还在内部用Rust开发了一组丰富的内部库,这些库围绕基于actor的并发范式而构建,这样利用Rust语言功能(例如async、await等)简化了异步编程。

  • Google

Google已将Rust应用到Chromium、Android和FuchsiaOS中,其中Chromium对Rust的支持处于实验阶段。开发者可以使用Rust来开发适用于Android和FuchsiaOS的组件,并且Rust在Android和FuchsiaOS的内部代码中使用的比例相当大,特别是FuchsiaOS,Rust代码已经超过50%。由于内部Cpp代码量较大,2022年10月,谷歌推出了基于开源RISC-V芯片的新型安全操作系统KataOS。Sparrow是KataOS的参考实现,运行在seL4上,几乎完全用Rust编写。该操作系统不是为台式电脑或智能手机设计的,而是为物联网设计的,可用于智能家居设备。目标是为嵌入式硬件或边缘设备构建可验证的安全操作系统,例如捕获图像的网络连接摄像头,这些图像在设备上或云中处理以进行机器学习。在2022年发布的Android 13版本中,谷歌还宣布Android版本13中大约21%的新原生代码(C/C++/Rust)是Rust。AOSP拥有约150万行Rust代码,涵盖了新功能和组件。此外,Android的Rust代码中已发现零内存安全漏洞。为了实现提高Android内部安全性、稳定性和质量的目标,Android团队还表示,Rust应该用在代码库中需要原生代码的任何地方。

  • 微软

Microsoft拥有世界上最大的用C/C++编写的代码集合之一,其所有核心产品(例如Windows、Office和Azure云)均使用该代码。2019年,微软开始探索内存安全的编程语言,并试用了Rust。随后,Rust for Windows Library在GitHub上开源,使Rust开发人员能够顺利使用Windows API。

2022年,微软Azure CTO Mark Russinovich表示,新项目不应再使用C和C++。他建议,Rust应该用于需要非GC语言的项目,以提高安全性和可靠性。

2023年7月,微软宣布在Windows 11 Insider Preview Build 25905版本中发布了Rust参与编写的Windows内核模块。其中包含了一个 GDI 引擎的实现。

  • Meta(前身为Facebook)

虽然不是创始成员,但财大气粗的Meta目前已经是唯一非创始成员的铂金赞助商了。Meta历史上以C++为主,但从2021年开始,Rust便开始大量使用Rust了,并成为Meta支持的服务器端语言列表中的最新成员。

Meta在2021和2022年先后发表过A brief history of Rust at FacebookProgramming languages endorsed for server-side use at Meta详细说明了Rust在Meta内部的应用,感兴趣的读者可以去看看。

  • Linux基金会

炒得沸沸扬扬的在Linux Kernel中支持Rust语言终于尘埃落定,Linux Kernel 6.1版本对Rust提供了支持。Rust同时进入Windows、Linux内核,这让Rust的江湖地位得到进一步提升。相信未来,Rust在两大操作系统内核中的代码比例会逐步提升的。

  • 其他一些公司对Rust的应用

2024年初,cloudflare公司开源了其内部替代nginx的Rust库pingora,作为业界一家提供互联网基础设施和网络服务的公司,其采用Rust的示范效应也是非常明显的。

influxdb的母公司influxdata在2023年发布了influxdb 3.0版本,该版本采用Rust全面重写。不光是influxdb,诸多新兴时序数据库都采用了Rust技术栈(+Arrow+Parquet+DataFusion),比如greptimedb、cnosdb、CeresDB等。

字节跳动内部服务大量使用Go,但这几年也有一些Rust爱好者在字节内部布道Rust,并开源了诸如Rust RPC框架volo、基于io-uring的Rust async runtime monoio等。

埃隆马斯克的xAI在2024年发布的grok-1大模型中,Rust开发的Qdrant向量数据库也发挥了重要作用,也是Rust在AI领域应用迈出的重要一步。

1.1.2.3 应用领域

在Rust官网,我们能看到官方列出的Rust应用的四大领域:

在这四个领域中,Rust都有非常活跃的发展和应用,每个领域都有大量的优秀开源项目,这里无法穷尽,大家可以参考与awesome-go类似的awesome-rust项目查看自己关于领域的开源项目。

1.1.2.4 工作机会与薪酬

devjobsscanner统计的2023年的各个编程语言的工作需求来看,Rust目前依旧比较小众!

stackoverflow 2023薪酬统计来看,Rust薪资位于中游:

另外4 day week的工作数量和薪酬分析也印证了上面两点:Rust小众(工作数量相对较少),薪酬位于中游:

国内Rust的工作数量与国际相同,都处于较少的位置,但国内Rust薪酬数据可能并不低,因为这些Rust岗位基本都在一线大厂,或是拿了较多融资的初创,待遇可能都比较不错。

了解了Rust的诞生和演化历史以及Rust的不错的现状后,我们再来看看Rust的设计哲学。

1.2 Rust的设计哲学

设计哲学之于编程语言,就好比一个人的价值观之于这个人的行为。因为如果你不认同一个人的价值观,那你其实很难与之持续交往下去,即所谓道不同不相为谋。类似的,如果你不认同一门编程语言的设计哲学,那么大概率你在后续的语言学习中,就会遇到上面提到的这些问题,而且可能会让你失去继续学习的精神动力。因此,在真正开始学习Rust语法和编码之前,我们还需要先来了解一下Rust的设计哲学,等了解完这些之后,你就能更深刻地认识到自己学习Rust的原因了。

1.2.1 Rust核心价值观

2019年6月,Rust核心组成员Stephen Klabnik在QCon London发表了一次名为How Rust Views Tradeoffs的演讲,在这次演讲中,他阐述了他个人理解的Rust的核心价值观,这些价值观是Rust team在做设计取舍时拒绝妥协的点,它们包括内存安全、执行速度和生产力:

按照Stephen Klabnik的说法,这三个核心价值观也是有序的,首先是内存安全,这是Rust最为在乎的立身之本,其次是高性能,最后是生产力。当它们之间出现冲突时,按最高价值观决策!

这其实与Rust官方对Rust的介绍也是一样的:

官方的Reliable对应的就是内存安全(memory safety),而efficient则有两层含义,一是运行时的高效,另外一个方面则是构建时的生产力也要保持高水准。

这三个价值观是Rust语言的设计目标,也是Rust语言的特色和优势所在。在失去了Graydon Hoare这个语言之父后,这些价值观也成为了Rust核心团队在判定语言演进方向的根本依据。

  1. 内存安全

内存安全是Rust最重要的价值观,它意味着Rust程序在运行时不会出现内存泄漏(不使用unsafe代码的前提下)、缓冲区溢出、野指针等内存相关的错误。这些错误不仅会导致程序崩溃,还可能导致安全漏洞的产生。Rust通过所有权(ownership)、生命周期(lifetime)和借用(borrowing)等特性,在编译时最大程度地检查出这些错误,从而保证程序的内存安全。

Rust的内存安全机制不仅能够提高程序的稳定性和可靠性,还能够降低开发和维护的难度。由于Rust能够在编译时就检查出内存错误,开发者就不必再花费大量时间和精力去寻找和修复这些错误了。

  1. 高性能

高性能是Rust的仅次于内存安全的一个核心价值观,Rust语言的设计目标之一就是要成为一种高性能的系统编程语言。Rust通过零成本抽象、移动语义、泛型编程等特性,使得程序能够在运行时达到与C、C++等传统系统编程语言相当的性能。

Rust的高性能机制不仅能够提高程序的运行速度,还能够降低硬件成本。由于Rust能够更好地利用硬件资源,因此在相同的硬件条件和资源开销下,Rust程序的性能通常比其他语言的程序更高。

  1. 生产力

生产力是Rust的第三个核心价值观,Rust语言的设计目标之一就是要成为一种能够提高开发者生产力的语言。Rust通过包管理器Cargo、智能编辑器支持、丰富的库生态、详实系统的文档等特性,使得开发者能够更轻松地编写、调试和维护Rust程序。

1.2.2 Rust的次要价值观

Stephen Klabnik还总结了三条Rust的次要价值观(secondary values):

我们看到:Rust的次要价值观包括ergonomics、compile times和correctness,这三个价值观也是Rust语言的设计目标之一,但和上面的第一级核心价值观相比,它们是可以被妥协掉的。

Ergonomics是指Rust语言的易用性,它是Rust语言的一个重要设计目标。Rust希望通过简单易用的语法和丰富的库生态,使得开发者能够更轻松地编写Rust程序。

Compile Times是指Rust编译器的编译时间。Rust编译器很慢,这是一个问题,Rust team也正在努力优化,但Rust team更关心二进制文件的最终执行速度,而不是让编译器变得更快,这就是Compile Time作为次要价值观的原因。

Correctness是指Rust语言的正确性,Rust真的很在乎你的程序是否正确,Rust希望通过强大的类型系统和静态检查,来尽可能地保证Rust程序的正确性。但Rust不愿意使用完全依赖类型以及证明助手来证明你的代码是正确的。

1.2.3 与Go的价值观的对比

我们来对比一下Go官方的对Go的介绍,看一下其隐含的Go价值观(设计哲学):

在官方对Go的介绍中有三个关键词:Simple、Secure和Scalable。

Simple是Go语言的首要设计原则,Go语言的设计者希望Go语言能够简单易用,使得开发者能够更快地学习和使用Go语言,以快速形成生产能力。Go语言的语法简单易懂,并且去掉了许多其他编程语言中复杂的特性,如类型层次与继承等,使得Go语言更加简洁易学、易读、易用和易维护。

至于Secure,Go语言的设计者希望Go语言能够更加安全可靠,避免许多其他编程语言中常见的安全漏洞。Go语言通过垃圾回收机制来自动管理内存,避免了许多其他编程语言中常见的内存泄漏和缓冲区溢出等问题。同时,Go语言提供了轻量级的goroutine和通道机制,使得开发者能够更加方便地实现并发编程,并且通过数据竞争检测工具,避免了并发编程中常见的数据竞争问题。同时Go语言提供了简单易用的显式错误处理机制,让开发者不遗漏任一处错误处理。

Scalable则体现在Go面向工程、原生内置并发以及崇尚组合的设计哲学上了。 Go语言的设计者希望Go语言能够更好地支持可扩展性,使得Go程序能够更好地适应不同的组织规模、不同的工作负载和硬件环境。Go语言通过简单的语法、基于module的可重现的构建管理、极高的编译速度、高质量的标准库、实用的工具链、强大的内置并发机制以及面向接口编程等特性,使得Go程序更加可扩展,生产力更为高效。

总的来说,Rust更注重安全、底层控制和极致性能,而Go则更加关注简单、安全、扩展性与工程效率。两者在定位和设计哲学上存在区别,但也有一些共同特点,比如都拥有现代的工具链、活跃的社区等。

1.3 本章小结

在这篇博文中,我们了解了Rust语言的诞生历程、现状发展,以及它独特的设计哲学。通过与Go语言进行对比,我们可以看出两者在出身、目标和设计理念上的一些差异。

随着软件系统的复杂度不断提高,对安全性、性能和并发的需求也越来越高。作为一门专注于底层系统编程、性能极致化的新语言,Rust正在吸引越来越多开发者的关注。相信通过后面对Rust的全方面的系统学习,我和大家都能够更深入地理解和掌握Rust。

如果你认为Rust的价值观与你的十分匹配,你也认同Rust未来的发展。那就期待下一篇吧,在下一篇中,我们将开始动手学习Rust了!

1.4 参考资料


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go 1.22中值得关注的几个变化

本文永久链接 – https://tonybai.com/2024/02/18/some-changes-in-go-1-22

美国时间2024年2月6日,正当中国人民洋溢在即将迎来龙年春节的喜庆祥和的气氛中时,Eli Bendersky代表Go团队在Go官博发文“Go 1.22 is released!”,正式向世界宣告了Go 1.22版本的发布!

注:大家可以从Go官网下载Go 1.22的第一个版本go 1.22.0,也可以在Go playground上选择Go 1.22版本在线体验Go 1.22的语法。

记忆中,这似乎是Eli Bendersky首次代表Go团队撰写Go版本发布的文章,文章短小且言简意赅,会让大家误以为Go 1.22版本没有太多的功能点变更,其实不然。读过我之前写的“Go 1.22新特性前瞻”一文的童鞋都知道Go 1.22中有很多重要且影响深远的值得我们关注的变化。在这篇文章中,我们就再来介绍一下这些变化,供大家参考。

0. 插播“旧闻”:Go再次进入Top10,并刷新有史以来的最高排名

TIOBE编程语言排行榜发布2024年2月编程语言排名的时间恰逢中国人民的传统佳节春节期间,因此它的这次排名发布“淹没”在了“龙年大吉”的喜庆气氛当中了。年后开工,大家翻看这条“旧闻”时,才发现在这次排名中,Go再一次回到Top10,位列第8名,刷新了Go打榜一来的历史最好位次。

单看这一次进入top10似乎没有什么,因为2023年4月份,Go也跻身过top10,排名第10。但如果从Go打榜以来的历史曲线来看,如下图:

我们看到了“翘尾”,我们看到了Go迈过“低谷”后的爬升!这与我在《Go语言第一课专栏》的结课语《和你一起迎接Go的黄金十年》中预判:Go即将迎来自己的黄金十年 愈来愈吻合了!

不过,我在《2023年Go语言盘点:稳中求新,稳中求变》一文中提到过TIOBE index作为世界最知名的编程语言排行榜,却存在其“不靠谱”的特性,比如这一期排名中,上古时代的编程语言Fortran从去年同期的第24位上升至第11位,仅比PHP落后一位,另一门古老的COBOL语言也从去年同期的第30位上升至第19位,仅仅比大热的Rust语言落后一位。

因此,对于TIOBE的排名,大家既要了解,也无需过于看重^_^。

言归正传,我们来说说Go 1.22版本的变化。

1. 语言变化

Go 1.22对语言语法做了两处变更,一个是Go 1.21版本中的试验特性loopvar在Go 1.22中转正落地;另一个也和for循环有关,那就是for range新增了对整型表达式的支持。两者相比较,还是第一个变化loopvar带来的影响更大一些。为什么呢?因此这是Go语言发展历史上第一次真正的填语义层面的“坑”,而且修改的是一个在Go源码中最常用的控制结构的执行语义,这很大可能会带来break change。Go101教程的作者老貘将之成为Go历史上最大的向后兼容性破坏版本

注:Go 1.21版本有一个对panic(nil)的语义修正,但我估计很少会有人写出panic(nil)这样的代码。

这次语义修改用一句话表达就是:将经典三段式for循环语句以及for range语句中的用短声明形式定义的循环变量从整个循环定义和共享一个,变为每个迭代定义一个

这里借用Go官博文章中那个例子再说明一下这个语义变化:

// go1.22-examples/lang/loopvar/main.go
package main

import (
    "fmt"
    "time"
)

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            time.Sleep(time.Second)
            fmt.Println(v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

我们用Go 1.22.0版本之前的版本,比如Go 1.21.0,来运行该示例:

$go run main.go
c
c
c

我们看到:由于v是整个循环中各个迭代共享的一个变量,所以在每个迭代新创建的goroutine中输出的v都是循环结束后v的最终值c。

如果我们用go 1.22.0来运行上述示例,我们将得到:

// 输出的值的顺序与goroutine调度有关
$go run main.go
b
c
a

注:关于Go 1.22版本之前的for range的坑,我的极客时间专栏《Go语言第一课》专栏有图文并茂的原理讲解,欢迎订阅阅读。

那么,loopvar这一语义填“坑”究竟会对你的代码造成怎样的影响呢?在Russ Cox关于loopvar语义变更的设计文档中提到了:只有go.mod中的go version在go 1.22.0及以后的时候才会生效,这是一个渐进式过渡的过程,因此目前无论是开源项目还是商业项目,只要go.mod中的go version还没有更新为大于等于go 1.22.0,那么for循环依然会保留短声明定义的变量的原语义,这样这些项目都不会受到影响。

不过,如果是直接在脚本中通过go run xxx.go形式运行某个go源码的,且当前工作目录以及父目录下没有go.mod文件的,go 1.22.0会采用新的loopvar语义,这点大家要注意了。

此外,当你将go.mod中的go version升级到go 1.22.0或更高版本时,也要注意语义变更可能带来的问题。在升级go version之前,可以用Go 1.22版本之前的go vet对项目源码进行一次静态分析,对于go vet提示:“loop variable v captured by func literal”的地方务必注意逐个确认。

注:Go 1.22版本中的go vet已经移除了在go version >= 1.22.0时,对“loop variable v captured by func literal”情况进行警告的功能。

关于Go 1.22中for range支持后面接整型表达式的“语法糖”新特性以及函数迭代器的实验特性,这里就不细说了,大家可以看看“Go 1.22新特性前瞻”一文中的说明。

2. 编译器、运行时与工具链

在编译器、运行时和工具链这些方面,Go 1.22的正式版本与“Go 1.22新特性前瞻”一文中使用的Go 1.22rc1版本几乎没有差异,这里挑主要内容介绍一下,其他一些内容可以参考前瞻一文。

Go 1.22版本继续在编译上优化PGO(profile-guided optimization), 基于PGO的构建可以比以前版本实现更高比例的调用去虚拟化(devirtualize)。在Go 1.22中,官⽅给出的PGO带来的性能提升数字是2%~14%,这应该是基于Google内部一些典型的Go程序测算出来的。

注:如果你对PGO优化还不是很了解,可以看看“深入理解Profile Guided Optimization(PGO)”这篇文章。

Go 1.22版本编译器现在可以更多运⽤devirtualize和inline。在Go编译器中,devirtualize是一种编译优化技术,旨在消除“虚函数”调用的开销。“虚函数”是指在面向对象编程中,通过基类指针或引用调用的函数。在Go中所谓虚函数调用指的就是通过接口类型变量进行的方法调用。由于是动态调用,基于接口的方法调用需要在运行时进行查找和分派,这可能导致一定的性能损失。

而Go编译器在进行devirtualize优化时,会尝试根据程序的上下文信息和类型信息,确定方法调用的具体对象实例。如果编译器能够确定调用的具体实例,则会将通过接口的方法调用替换为直接调用具体对象实例的方法,从而消除运行时的开销,使得通过接口类型变量进行方法调用的性能得到优化提升。

Go 1.22版本中的运行时可以使基于类型的垃圾收集的元数据更接近每个堆对象,从而将Go程序的CPU性能(延迟或吞吐量)提高了1-3%。这一变化还支持通过重复数据删除冗余元数据,进而将大多数Go程序的内存开销减少了大约1%。

在工具链方面,有三个主要改变这里提一下:

  • go work支持vendor

在Go 1.22版本中,通过go work vendor可以将workspace中的依赖放到vendor⽬录下,同时在构建时,如果workspace下有vendor⽬录,那么默认的构建是go build -mod=vendor,即基于vendor的构建。

  • go mod init不再care其他vendor工具的配置文件

go mod init不再尝试将其他vendor工具(例如Gopkg.lock )的配置文件导入到go module依赖文件(go.mod)中了,也就是说从Go 1.22版本开始,go module出现之前的那些gopath时代的依赖管理工具正式退出并成为历史了。

  • 改进go test -cover的输出

对于没有自己的测试文件的包,go test -cover在go 1.22版本之前会输出:

? mymod/mypack [no test files]

但在Go 1.22版本之后,会报告覆盖率为0.0%:

mymod/mypack coverage: 0.0% of statements

3. 标准库

这里列举一下标准库值得关注的重大变化,大家可以与前瞻一文相互参考着阅读。

3.1 math/rand/v2:标准库的第一个v2版本包

Go 1.22中新增了math/rand/v2包,这里之所以将它列为Go 1.22版本标准库的⼀次重要变化,是因为这是标准库第一次为某个包建⽴v2版本,按照Russ Cox的说法,这次math/rand/v2包的创建,算是为标准库中的其他可能的v2包“探探路”,找找落地路径。关于math/rand/v2包相对于原math/rand包的变化有很多,具体可以参考issue 61716中的设计与讨论。

3.2 增强http.ServeMux表达能力

在Go 1.22版本中,http.ServeMux的表达能力得到了大幅提升,从原先只支持静态路由,到新版本中支持如下一些特性:

  • “/index.html”路由将匹配任何主机和方法的路径”/index.html”;
  • “GET /static/”将匹配路径以”/static/”开头的GET请求;
  • “example.com/”可以与任何指向主机为”example.com”的请求匹配;
  • “example.com/{$}”会匹配主机为”example.com”、路径为”/”的请求,即”example.com/”;
  • “/b/{bucket}/o/{objectname…}”匹配第一段为”b”、第三段为”o”的路径。名称”bucket”表示第二段,”objectname”表示路径的其余部分。

并且新版ServeMux在路由匹配性能方面也不输众多开源http路由框架太多,后续建立Go web或api类新项目时,可以考虑首选新版ServeMux来进行路由匹配了,减少对外部的一个依赖。

关于新版http.ServeMux的具体使用方法,其作者Jonathan Amsterdam(也是log/slog的作者)在官博发表了一篇名为“Routing Enhancements for Go 1.22”的文章,大家可以详细参考。

关于标准库的其他一些变化,大家可以参考前瞻一文以及更详细的Go 1.22的发布说明文档

4. 小结

综上,Go 1.22版本对语言、编译器、工具链、运行时和标准库都有一定程度的改进和创新,遗留代码通过Go 1.22版本的重新编译便可以得到一定程度的性能上的自然提升,这也体现了Go语言在稳中求新、稳中求变的特点。

不过这里还要提醒各位Go开发者,在升级Go 1.22版本时务必注意潜在的向后兼容性问题,尤其是loopvar语义带来的变化影响。

本文涉及的源码可以在这里下载。


Gopher部落知识星球在2024年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。同时,我们也会加强代码质量和最佳实践的分享,包括如何编写简洁、可读、可测试的Go代码。此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats