Go 1.18新特性:范型

2022年3月7日 307点热度 0人点赞 0条评论

图片


Go语言的1.18版本还未正式发布,但我们已经可以用它的RC版本尝试一些新特性。今天来聊聊Go 1.18里的范型(Generics)。

安装

Generics是1.18的新特性,在目前的发布版1.17里不支持,因此需要手动安装RC版:

go install golang.org/dl/go1.18rc1@latest

安装完后,你的GOPATH或者GOBIN里会出现一个go1.18rc1的可执行文件。然后下载SDK:

go1.18rc1 download

此后编译和运行go代码,需要使用这个可执行文件,或者你也可以用alias把go指向这个RC版本,这样就不用每次打版本号了:

alias go=go1.18rc1

使用Generics

看一个简单的例子,假如我们要实现一个函数,输入是一个名字(string)到整数数值(int64)的映射表,返回是这个映射表里所有数值的总和:

func SumInts(m map[string]int64) int64 {    var s int64    for _, v := range m {        s += v    }    return s}

很简单的实现。但是,如果我们想要也能处理浮点数值(float64)呢?是否要写个类似的函数:

func SumFloats(m map[string]float64) float64 {  var s float64  for _, v := range m {    s += v  }  return s}

这样做也不是不行,但是有很多重复代码,使用起来也不方便。

看看用Generics的实现:

func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {  var s V  for _, v := range m {    s += v  }  return s}

解释一下:

  • 方括号里的部分,声明了类型参数:[K comparable, V int64 | float64],这里声明了两个类型,K是内置的comparable类型,任何类型只要能进行比较(==, !=)就属于comparable;V可以是int64或float64之一,也就是这两个类型的union,V这样的又称为“类型限制”(type constraint),因为它限制了V可能的类型。

  • 然后在函数的参数部分,用这两个类型K和V来声明参数:m map[K]V,也就是说输入参数是一个类型K到类型V的映射表。因为map要求key必须是可比较的,这里K类型声明为comparable就保证了这一点。

  • 函数的返回值,也是类型V。

这样,我们只需要一个实现就能支持多种类型了。

如何调用这个范型函数?也很简单:

ints := map[string]int64{    "first":  12,    "second"34,}
floats := map[string]float64{    "first":  12.34,    "second"22.55,}
fmt.Printf("SumInts: %v\n", SumIntsOrFloats[string, int64](ints))
fmt.Printf("SumFloats: %v\n", SumIntsOrFloats[string, float64](floats))

在这个例子里,我们调用范型函数的时候加上了类型参数,例如[string, int64]。实际上,这个类型参数在很多时候是可以省略的,Go会根据函数的参数自动推导类型:

fmt.Printf("SumInts: %v\n",           SumIntsOrFloats(ints))
fmt.Printf("SumFloats: %v\n", SumIntsOrFloats(floats))

当然这种自动类型推导并不是在所有情况下都可用,比如如果函数没有参数,那就没法推导了,调用时必须加上类型参数。

另一个比较好的习惯是,把范型函数的类型参数提取到interface里,可以提高代码的可读性和可维护性:

type Number interface {  int64 | float64}
func SumNumbers[K comparable, V Number](m map[K]V) V { var s V for _, v := range m { s += v } return s}

这里把V的类型提取到Numbers这个interface里,Numbers里定义了类型限制(int64或float64)。这样范型函数SumNumbers就更简洁,并且如果还有其他的范型函数用到它的话,我们不用到处复制 int64 | float64,可维护性大大提高。

Go 1.18的Generics特性就先聊到这里。感谢阅读! 

73740Go 1.18新特性:范型

这个人很懒,什么都没留下

文章评论