Go 官方博客近日发表了一篇介绍新特性“泛型”的文章,作者是两位重量级人物 —— Robert Griesemer 和 Ian Lance Taylor,内容基于他们在 2021 年 GopherCon 大会上的演讲。
▲ 左:Go 语言设计者之一 Robert Griesemer;右:Go 泛型主要设计者 Ian Lance Taylor
视频地址:
不久前正式发布的 添加了对泛型的支持,据称泛型是 Go 开源以来所做的最大改变。泛型是一种编程范式,这种范式独立于所使用的特定类型,泛型允许在函数和类型的实现中使用某个类型集合中的任何一种类型。
泛型为 Go 添加了三个新的重要内容:
面向函数和类型的“类型形参” (type parameters)
将接口类型定义为类型集合,包括没有方法的接口类型
类型推断:在大多数情况下,在调用泛型函数时可省略“类型实参” (type arguments)
Type Parameters
现在函数和类型都具有类型形参” (type parameters),类型形参列表看起来就是一个普通的参数列表,除了它使用的是方括号而不是小括号。
先从浮点值的基本非泛型 Min 函数开始:
func Min(x, y float64) float64 {
if x y {
return x
return y
}
通过添加类型形参列表来使这个函数泛型化——使其适用于不同的类型。在此示例中,添加了一个带有单个类型形参T的类型参数列表,并替换了float64。
import "golang.org/x/exp/constraints"
func GMin[T constraints.Ordered](x, y T) T {
if x y {
return x
return y
}
然后就可以使用类型实参调用此函数:
x := GMin[int](2, 3)
向GMin提供类型参数,在这种情况下int称为实例化。实例化分两步进行。首先,编译器在泛型函数或泛型类型中用所有类型形参替换它们各自的类型实参。然后,编译器验证每个类型形参是否满足各自的约束。如果第二步失败,实例化就会失败并且程序无效。
成功实例化后,即可产生非泛型函数,它可以像任何其他函数一样被调用。比如:
fmin := GMin[float64]
m := fmin(2.71, 3.14)
GMin[float64]的实例化产生了一个与Min函数等效的函数,可以在函数调用中使用它。类型形参也可以与类型一起使用。
type Tree[T interface{}] struct {
left, right *Tree[T]
value T
func (t *Tree[T]) Lookup(x T) *Tree[T] { ... }
var stringTree Tree[string]
在上面的例子中,泛型类型Tree存储了类型形参T的值。泛型类型也可以有方法,比如本例中的Lookup。为了使用泛型类型,它必须被实例化;Tree[string]是使用类型实参string来实例化Tree的示例。
类型推断
此项功能是最复杂的变更,主要包括:
函数参数类型推断 (Function argument type inference)
约束类型推断 (Constraint type inference)
虽然类型推断的工作原理细节很复杂,但使用它并不复杂:类型推断要么成功,要么失败。如果它成功,类型实参可以被省略,调用泛型函数看起来与调用普通函数没有什么不同。如果类型推断失败,编译器将给出错误消息,在这种情况下,只需提供必要的类型实参。
泛型是 Go 1.18 的重要新语言特性,Robert Griesemer 和 Ian Lance Taylor 表示,这个功能实现得很好并且质量很高。虽然他们鼓励在有必要的场景中使用泛型,但在生产环境中部署泛型代码时,请务必谨慎。
。