ipfans's Blog

Recent content on ipfans's Blog

马上订阅 ipfans's Blog RSS 更新: https://www.4async.com/atom.xml

Go 1.17 泛型尝鲜

2021年8月18日 01:48
Featured image of post Go 1.17 泛型尝鲜

今天,Go的1.17版本终于正式发布,除了带来各种优化和新功能外,1.17正式在程序中提供了尝鲜的泛型支持,这一功能也是为1.18版本泛型正式实装做铺垫。意味着在6个月后,我们就可以正式使用泛型开发了。那在Go 1.18正式实装之前,我们在1.17版本中先尝鲜一下泛型的支持吧。

泛型有什么作用?

在使用Go没有泛型之前我们怎么实现针对多类型的逻辑实现的呢?有很多方法,比如说使用interface{}作为变量类型参数,在内部通过类型判断进入对应的处理逻辑;将类型转化为特定表现的鸭子类型,通过接口定义的方法实现逻辑整合;还有人专门编写了Go的函数代码生成工具,通过批量生成不同类型的相同实现函数代替手工实现等等。这些方法多多少少存在一些问题:使用了interface{}作为参数意味着放弃了编译时检查,作为强类型语言的一个优势就被抹掉了。同样,无论使用代码生成还是手工书写,一旦出现问题,意味着这些方法都需要重复生成或者进行批量修改,工作量反而变得更多了。

在Go中引入泛型会给程序开发带来很多好处:通过泛型,可以针对多种类型编写一次代码,大大节省了编码时间。你可以充分应用编译器的编译检查,保证程序变量类型的可靠性。借助泛型,你可以减少代码的重复度,也不会出现一处出现问题需要修改多处地方的尴尬问题。这也让很多测试工作变得更简单,借助类型安全,你甚至可以少考虑很多的边缘情况。

Go语言官方有详细的泛型提案文档可以在这里这里查看详情。

如何使用泛型

前面理论我们仅仅只做介绍,这次尝鲜还是以实践为主。让我们先从一个小例子开始。

从简单的例子开始

让我们先从一个最简单的例子开始:

package main

import (
 "fmt"
)

type Addable interface {
 type int, int8, int16, int32, int64,
 uint, uint8, uint16, uint32, uint64, uintptr,
 float32, float64, complex64, complex128,
 string
}

func add[T Addable](a, b T) T {
 return a + b
}

func main() {
 fmt.Println(add(1,2))
 fmt.Println(add("1", "2"))
}

这个函数可以实现任何需要使用+符号进行运算的类型,我们通过定义Addable类型,枚举了所有可能可以使用add方法的所有的类型。比如我们在main函数中就使用了intstring两种不同类型。

但是如果这时我们使用简单的go run命令运行,会发现提示语法错误:

$ go version
go version go1.17 darwin/arm64
$ go run ~/main.go
# command-line-arguments
../main.go:8:2: syntax error: unexpected type, expecting method or interface name
../main.go:15:6: missing function body
../main.go:15:9: syntax error: unexpected [, expecting (

因为在Go 1.17中,泛型并未默认开启,你需要定义gcflags方式启用泛型: