Go 1.18 终于迎来了 RC 版本,离正式发布越来越近了。据悉,Go 1.18 预计会再发一个 RC2,然后就是正式版本了,正式版本预计 3 月份发布。
语言的变化
通用语
Go 1.18 包括实现类型参数提案所描述的泛型功能。这是一个重大的变化,新的语言变化需要大量的新代码,这些代码还没有在生产环境中进行过大量的测试,因此建议开发者在生产环境中部署泛型代码时保持适当的谨慎态度。
- 函数和类型声明的语法现在可以接受类型参数。
- 参数化的函数和类型可以通过在方括号中的类型参数列表后面进行实例化。
- 新的标记
~
已经被添加到运算符和标点符号的集合中。
- 接口类型的语法现在允许嵌入任意类型(不仅仅是接口的类型名称)以及union和
~T
类型元素。这种接口只能作为类型约束使用。一个接口定义了一组类型以及一组方法。
- 新的预先声明的标识符
any
是空接口的别名。它可以用来代替interface{}
。
- 新的预定义标识符
comparable
是一个接口,表示所有类型的集合,可以使用==
或!=
进行比较。它只能作为(或嵌入)一个类型约束来使用。
有三个使用泛型的实验性包可能是有用的。这些包在x/exp资源库中;它们的API不在Go 1的保证范围内,不过,随着对泛型的经验积累,可能会发生变化。
golang.org/x/exp/constraints
对通用代码有用的约束条件,如 constraints.Ordered
.
golang.org/x/exp/slices
一组可以对任何元素类型的片子进行操作的泛型函数。
golang.org/x/exp/maps
一组对任何键或元素类型的地图进行操作的泛型函数。
目前的泛型实现有以下限制。
- Go编译器目前不能处理泛型函数或方法中的类型声明。在Go 1.19中有望提供对该功能的支持。
- Go编译器目前不接受预先声明的函数
real
,imag
, 和complex
的参数类型的参数。我们希望在Go 1.19中取消这一限制。
- 不允许将类型参数或指向类型参数的指针嵌入结构体类型中的未命名字段。同样,在接口类型中嵌入一个类型参数也是不允许的。目前还不清楚这些是否会被允许。
- 有一个以上术语的联盟元素不能包含一个具有非空方法集的接口类型。这是否会被允许,目前还不清楚。
错误修正
Go 1.18编译器现在可以正确报告declared but not used
,在此之前,编译器不会报告错误。这修复了长期存在的编译器问题#8560。由于这一变化,(可能是不正确的)程序可能无法再编译了。必要的修正是直接的:如果程序确实不正确,就修正它,或者使用违规的变量,例如把它赋值给空白标识符_
。由于go vet
总是指出这个错误,受影响的程序数量可能非常小。
Go 1.18 编译器现在将符文常数表达式(如'1' << 32
)作为参数传递给预先声明的函数print
和println
时报告溢出,这与用户定义的函数行为一致。在Go 1.18之前,编译器在这种情况下并不报错,而是默默地接受这种常量参数,如果它们符合int64
。由于这一变化,(可能是不正确的)程序可能不再被编译。必要的修正是直接的:如果程序事实上是不正确的,就修正它,或者明确地将违规的参数转换为正确的类型。由于go vet
总是指出这个错误,受影响的程序数量可能非常少。
工具类
模糊处理
Go 1.18 包含了fuzzing 提案所描述的 fuzzing 实现。
请注意,fuzzing 会消耗大量内存,在运行时可能会影响机器的性能。在运行过程中,模糊引擎会将扩大测试范围的数值写入$GOCACHE/fuzz
内的模糊缓存目录。目前对写入模糊缓存的文件数量或总字节数没有限制,所以它可能会占用大量的存储空间。
Gofmt
gofmt
现在可以并发地读取和格式化输入文件,其内存限制与GOMAXPROCS
成正比。在有多个CPU的机器上,gofmt
,现在应该会明显加快。
Vet
对泛型的更新
vet
工具被更新以支持通用代码。在大多数情况下,只要在非泛型代码中用其类型集中的类型替换类型参数后,它就会报告泛型代码的错误。例如,vet
中报告了一个格式错误。
func Print[T ~int|~string](t T) {
fmt.Printf( %d , t)
}
它会报告非通用等价的格式错误 Print[string]
:
func PrintString(x string) {
fmt.Printf( %d , x)
}
现有检查器的精度改进
cmd/vet
检查器copylock
,printf
,sortslice
,testinggoroutine
和tests
都进行了适度的精度改进,以处理额外的代码模式。不过,这可能会导致现有软件包中出现新的报告错误。
// fmt.Printf formatting directive %d is being passed to Println.
fmt.Println( %d +` ≡ x (mod 2)`+ \n , x%2)
运行时间
在确定运行频率时,垃圾收集器现在会包括垃圾收集器工作的非堆源(例如堆栈扫描)。因此,当这些来源很重要时,我们可以更轻松预测垃圾收集器的开销。对于大多数应用程序,这些更改可以忽略不计,不过,一些 Go 应用程序在垃圾收集的方面,可能比以前使用更少的内存并花费更多时间,预期的解决方法是 GOGC
在必要时进行调整。
运行时现在更有效地将内存返回给操作系统,。
Go 1.17 改进了堆栈跟踪中参数的格式,但可能会为寄存器中传递的参数打印不准确的值。在 Go 1.18 中会在每个可能不准确的值后打印一个问号 。
编译器
Go 1.17实现了一种新的方法,即在选定的操作系统 64 位 x86 架构上使用寄存器而不是堆栈传递函数参数和结果。Go 1.18扩展了支持的平台,包括64位ARM (GOARCH=arm64
)、big- and little-endian 64位PowerPC (GOARCH=ppc64
,ppc64le
),以及所有操作系统上的64位x86架构 (GOARCH=amd64
)。在64位ARM和64位PowerPC系统上,基准测试显示典型的性能改进为10%或更多。
编译器现在可以内联包含范围循环或标记的for循环的函数。
新的-asan
编译器选项支持新的go
命令-asan
选项。
由于编译器的类型检查器被全部替换为支持泛型,一些错误信息现在可能会使用与以前不同的措辞。在某些情况下,Go 1.18之前的错误信息提供了更多的细节或以更有帮助的方式来表述。
由于编译器中与支持泛型有关的变化,Go 1.18的编译速度可能比Go 1.17的编译速度大约慢15%。编译后的代码执行时间不受影响。
Go 1.18 Release Notes:tip.golang.org/doc/go1.18