原创分享 Go 1.17 中值得关注的几个变化

bigwhite-github · 2021年08月19日 · 670 次阅读
本帖已被设为精华帖!

本文永久链接 - https://tonybai.com/2021/08/17/some-changes-in-go-1-17

Go 核心开发团队在去年 GopherCon 大会上给 Go 泛型的定调是在 2022 年 2 月份的 Go 1.18 版本中发布,那可是自Go 诞生以来语法规范变动最大的一次,这让包括笔者在内的全世界的 Gopher 们都满怀期待。

不过别忘了,在 Go 1.18 这个 “网红版本” 发布前,还有一个 “实力派” 版本 Go 1.17 呢!美国当地时间2021年8月16日,Go 1.17 版本在经过两个 RC 版本之后正式发布!并且值得庆幸的是 Go 1.17 版本并没有过多受到 Go 1.18 版本这个 “网红” 的影响,Go 1.17 默默地加入和优化了着实不少的特性。在这一篇文章中,我们就来看看 Go 1.17 版本中有哪些值得关注的变化。

1. 语言特性变化

Go 属于那种极简的语言,从诞生到现在语言自身特性变化很小,不会像其他主流语言那样走 “你有的我也要有” 的特性融合路线。因此新语言特性对于 Gopher 来说属于 “稀缺品”,属于 “供不应求” 那类事物^_^。这也直接导致了每次 Go 新版本发布,我们都要首先看看语言特性是否有变更,每个新加入语言的特性都值得我们去投入更多关注,去深入研究。Go 1.17 在语言特性层面做了两方面的小改动,下面我们来看看。

第一个是对语言类型转换规则的扩展,允许从切片到数组指针的转换,下面的代码在 Go 1.17 版本中是可以正常编译和运行的:

// github.com/bigwhite/experiments/tree/master/go1.17-examples/lang/slice2arrayptr/main.go
func slice2arrayptr() {
    var b = []int{11, 12, 13}
    var p = (*[3]int)(b)
    p[1] = p[1] + 10
    fmt.Printf("%v\n", b) // [11 22 13]
}

Go 通过运行时对这类切片到数组指针的转换代码做检查,如果发现越界行为,就会通过运行时 panic 予以处理。Go 运行时实施检查的一条原则就是 “转换后的数组长度不能大于原切片的长度”,注意这里是切片的长度 (len),而不是切片的容量 (cap)。

第二个变动则是 unsafe 包增加了两个函数:Add 与 Slice。使用这两个函数可以让开发人员更容易地写出符合unsafe 包使用的安全规则的代码。这两个函数原型如下:

// $GOROOT/src/unsafe.go
func Add(ptr Pointer, len IntegerType) Pointe
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType

unsafe.Add 允许更安全的指针运算,而 unsafe.Slice 允许更安全地将底层存储的指针转换为切片。

2. go module 的变化

Go 1.11 版本引入 go module以来,每个 Go 大版本发布时,go module 都会有不少的积极变化,这是 Go 核心团队与社区就 go module 深入互动的结果。

Go 1.17 中 go module 同样有几处显著变化,其中最最重要的一个变化就是 pruned module graph(修剪的 module 依赖图)。Go 1.17 之前的版本某个 module 的依赖图由该 module 的直接依赖以及所有间接依赖组成,无论某个间接依赖是否真正为原 module 的构建做出贡献,这样 go 命令在解决依赖时会读取每个依赖的 go.mod,包括那些没有被真正使用到的 module,这样形成的 module 依赖图被称为完整 module 依赖图(complete module graph)

Go 1.17 不再使用 “完整 module 依赖图”,而是引入了 pruned module graph(修剪的 module 依赖图)。修剪的 module 依赖图就是在完整 module 依赖图的基础上将那些 “占着茅坑不拉屎”、对构建完全没有 “贡献” 的间接依赖 module 修剪后的依赖图。使用修剪后的 module 依赖图进行构建将有助于避免下载或阅读那些不必要的 go.mod 文件,这样 Go 命令可以不去获取那些不相关的依赖关系,从而在日常开发中节省时间。

但 module 依赖图修剪也带来了一个副作用,那就是 go.mod 文件 size 的变大。因为 Go 1.17 版本后,每次 go mod tidy(当 go.mod 中的 go 版本为 1.17 时),go 命令都会对 main module 的依赖做一次深度扫描 (deepening scan),并将 main module 的所有直接和间接依赖都记录在 go.mod 中(之前说的版本只记录直接依赖)。考虑到内容较多,go 1.17 将直接依赖和间接依赖分别放在两个不同的 require 块儿中。

3. 编译器与运行时的变化

Go 1.17 增加了对 Windows 上 64 位 ARM 架构的支持,让开发者可以在更多设备上原生运行 Go。但这个版本编译器最大的变化是在 amd64 架构下率先实现了从基于堆栈的调用惯例到基于寄存器的调用惯例切换

并且,切换到基于寄存器的调用惯例后,一组有代表性的 Go 包和程序的基准测试显示,Go 程序的运行性能提高了约 5%,二进制文件大小典型减少约 2%。也就是说你的 Go 源码使用 Go 1.17 版本重新编译一下就能获得大约 5% 的性能提升,真希望这样的优化越多越好!对更多平台的基于寄存器调用惯例的支持将在未来的版本中出现。

除了改为基于寄存器的调用惯例之外,Go 1.17 编译器还支持包含闭包的函数的内联 (inline) 了!这样一来,一个带有闭包的函数可能会在函数被内联的每个地方产生一个不同的闭包代码指针,因此, Go 函数的值不能直接比较

Go 编译器还在 Go 1.17 中引入了//go:build 形式的构建约束指示符,以替代原先易错的// +build 形式。

4. 其他变化

  • 保留龙芯架构 GOARCH 值

在 Go 1.17 版本中,Go 编译器保留了中国龙芯 cpu 架构的 GOARCH 值 - loong64。关于龙心 GOARCH 值选用 loong64 还是 loongarch64 还有过一段激烈的争论,最终大多数都赞同的 loong64 取得了最后的胜利。

  • Go test 变化

Go test 引入-shuffle 的洗牌标志位,用以控制单元测试或 benchmark 的执行顺序。

另外 T 和 B 两个类型分别都增加了 Setenv 方法用于在 test 和 benchmark 执行期间设置环境变量。

  • time 包增加 Time 对象的 GoString 形式输出

我们使用%#v 输出一个 Time 对象实例时,Go 1.17 之前的版本输出内容如下面:

Go 1.16.5 输出:

time.Time{wall:0xc03f08c0d06c9ed0, ext:83078, loc:(*time.Location)(0x11620e0)}

Go 1.17 增加了 GoString 方法,该方法在 Time 对象以%#v 格式输出时被自动调用,其输出结果如下:

time.Date(2021, time.August, 17, 20, 29, 42, 58245000, time.Local)

5. 小结

除上述变化之外,Go 的其他标准库随着新版本的发布也都会有大量的小改动,但每个开发人员对标准库的关注点差别很大,因此,在这个系列中不会详细做说明了,大家还是参考Go 1.17 的发布说明文档各取所需吧^_^。

与传统的 “Go 新版本值得关注的几个变化” 系列有所不同,本期内容较为简单和概括,因为更多内容,我将在后续的Go 1.17 新特性详解系列中针对上述值得关注的新特性做进一步说明。详解系列已经写好,不过首发在了本人运营的星球 “Gopher 部落”上了,如果你迫切想深入了解这些新特性,可以加入星球阅读。

本文所涉及的源码可以在这里 - https://github.com/bigwhite/experiments/tree/master/go1.17-examples/


Go 技术专栏 “改善 Go 语⾔编程质量的 50 个有效实践” 正在慕课网火热热销中!本专栏主要满足广大 gopher 关于 Go 语言进阶的需求,围绕如何写出地道且高质量 Go 代码给出 50 条有效实践建议,上线后收到一致好评!欢迎大家订 阅!

img{512x368}

Gopher Daily(Gopher 每日新闻) 归档仓库 - https://github.com/bigwhite/gopherdaily

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
astaxie 将本帖设为了精华贴 08月19日 03:19
ningxiaofang1o GoCN 每日新闻 (2021-08-22) 中提及了此贴 08月22日 04:14
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册