Golang 1.17.x泛型体验

前言

Golang在1.17.x之前的版本都不支持泛型。缺少了泛型的支持,导致平时在开发过程中,不得不针对不同的类型去做一些代码的复制粘贴。比如,有个函数是打乱slice,没有泛型就只能对不同类型的slice写不同参数的函数,或者反射骚操作。虽然有些场景可以通过反射操作解决问题,但是还是不如泛型来的方便。这次打算体验一波1.17.x的泛型,看一下是否实用,再研究一下一下泛型函数的性能。

升级环境&配置

这个月1.17的golang已经到了1.17.3了,直接到官网下载最新的安装包。
官网版本信息
不知道是不是太久没升级go的版本了,Windows默认go root跑到了program files下,goland需要在设置中重新选一下go root

自用的goland是2021.2.3,这个版本已经是最新的版本了。不过现在泛型还属于实验性的功能,输入泛型的语法会出现如下提示:

1
Generics (experimental support for type parameters) are disabled. Unresolved type 'any'.

goland泛型设置

虽然在goland的设置中开启Enable generics可以消除上面的警告,但是语法高亮还是有问题,编译时也没有加入参数,感觉开启了个寂寞。
运行还是要加入-gcflags=-G=3的参数
go编译参数

小试牛刀

1
2
3
4
5
6
7
8
9

import "fmt"
func test[T any](t T)  {
    fmt.Println(t)
}
func main() {
    test(1)
    test("test")
}

虽然goland提示有语法问题,但是还是输出了结果。
运行结果

目前局限

当把上面的函数改为大写开头的导出(export)函数时,发生了报错。

1
2
3
4
5
6
# command-line-arguments
.\generics.go:5:6: internal compiler error: Cannot export a generic function (yet): Test


Please file a bug report including a short program that triggers the error.
https://golang.org/issue/new

也就是说目前1.17.x experimental的泛型只支持在包内,无法导出给其他包使用。

探寻原理

golang程序,最直接查看原理的方式就是看汇编了,拿go tool生成一波汇编代码。为了减少生成的汇编代码,把上面的fmt.Println换成函数println。

1
go tool compile -S -G=3 -N -l generics.go > generics.S

一开始没关掉编译优化的时候,生成的汇编代码直接在main函数里打印了,关掉优化后的汇编代码则是调用了生成的两个函数。

1
2
3
4
5
6
    0x0014 00020 (generics.go:8)    MOVL    $1, AX
    0x0019 00025 (generics.go:8)    PCDATA    $1, $0
    0x0019 00025 (generics.go:8)    CALL    "".test[int](SB)
    0x001e 00030 (generics.go:9)    LEAQ    go.string."test"(SB), AX
    0x0025 00037 (generics.go:9)    MOVL    $4, BX
    0x002a 00042 (generics.go:9)    CALL    "".test[string](SB)

生成的函数

1
2
"".test[int] STEXT size=77 args=0x8 locals=0x10 funcid=0x0
"".test[string] STEXT size=99 args=0x10 locals=0x18 funcid=0x0

也就是说golang的泛型函数是通过生成不同参数的函数实现的,这样看来golang的泛型只会影响到编译速度,对运行时的性能应该没有啥影响。

前瞻开发分支

开发分支运行结果
golang.org上有gotip版本的playground,可以看到gotip版本的playground已经支持导出函数了。

小结

可以说Golang 1.17.x的泛型只是给大家一个体验版。一般来说用得到泛型的一般是作为一些公共&基础的内容放在公共的包中,目前无法导出函数也就意味着无法跨包使用。
不过既然是生成多个参数的函数,那么不支持跨包使用应该只是1.17.x编译器的问题了,未来泛型转正的时候应该就能够支持了。
不知道为啥1.17.x的go test传入-gcflags=-G=3没有反应,所以这次也没有跑一下benchmark,暂且不去下载开发分支的go测试了。
目前来看,现在的泛型也不适合在生产环境使用吧。不支持跨包,还有毕竟是实验性的特性不能保证没有坑,支持跨包开发分支的版本就更不敢拿在生产环境使用了。
期待正式的1.18。

作者

ZhongHuihong

发布于

2021-11-21

更新于

2021-11-21

许可协议