早在两年前,我就在 Go 夜读做过一个关于 Go 语言泛型设计的演讲,在那个演讲里我介绍了 Go 语言泛型设计的进化过程,当时泛型还是基于合约 contracts 的设计。虽然如今已经又有了一些变化,并演化为基本定稿的类型参数(Type Parameters)和类型集(Type Sets)设计,但实现泛型的三大基本思想并没有变:
1)使用类型作为泛型实体的形式参数;
2)使用类型集合来作为调用方实际类型参数的约束并进行类型检查;
3)利用类型推导来简化使用成本。
更多的内容可以参见:https://golang.design/s/go2generics 。
这两页演讲稿基本上总结了最新的设计。这是类型参数:
这是类型集:
那么类型参数和类型集有些什么更具体的例子呢?当时我就曾开过一个 GitHub 的仓库用来归档一些实验性的泛型代码。最近因为 Go 语言的编译器(1.18 开发版)已经默认开启泛型的编译,所以我把这个仓库里面的代码做了一次升级,已经完整兼容最新的语法了。这个仓库是:
https://github.com/golang-design/go2generics
你可能会好奇怎么提前尝试,下面是一篇来自 Go 夜读的文章,对这个实践过程进行了介绍。
go 1.17 的泛型支持有限,慎重使用
不久前,Go 1.17 正式发布。其实在 Go 1.17 中,Go 团队已经实现了 Go: Type Parameters Proposal ,并且使用类似 GO11MODULE 开关的方式提供 Go 泛型的支持,默认为关闭(-gcflags=-G=0
),但是我们可以打开,具体方法如下:
go build -gcflags=-G=3
就在刚刚-gcflags=-G=3
默认打开的开关被合入 master,相关 CL: 343732: cmd/compile: enable -G=3 by default | https://go-review.googlesource.com/c/go/+/343732 [1] 。
这意味着什么呢?master 构建的 go 默认就支持泛型了,那我们怎么方便的使用呢?答案就是 gotip 。
支持泛型的 Go 编译器 gotip
安装 gotip[2] 之前,必须先安装 go,本文就不详述了,安装有问题可以自行搜索,或者私信我。
-
gotip 工具安装
$ go get golang.org/dl/gotip
-
下载安装 master 代码
$ gotip download
Updating the go development tree...
remote: Finding sources: 100% (19158/19158)
remote: Total 19158 (delta 10570), reused 16946 (delta 10570)
Receiving objects: 100% (19158/19158), 32.20 MiB | 1.24 MiB/s, done.
Resolving deltas: 100% (10570/10570), completed with 414 local objects.
From https://go.googlesource.com/go
* branch master -> FETCH_HEAD
8f676144ad..6e50991d2a master -> origin/master
Updating files: 100% (2868/2868), done.
Previous HEAD position was 8f676144ad crypto/rsa: fix salt length calculation with PSSSaltLengthAuto
HEAD is now at 6e50991d2a strconv: reject surrogate halves in Unquote
Building Go cmd/dist using /Users/maiyang/develop/go1.16.6. (go1.16.6 darwin/amd64)
Building Go toolchain1 using /Users/maiyang/develop/go1.16.6.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for darwin/amd64.
---
Installed Go for darwin/amd64 in /Users/maiyang/sdk/gotip
Installed commands in /Users/maiyang/sdk/gotip/bin
Success. You may now run 'gotip'!
-
检查 gotip 版本
$ gotip version
go version devel go1.18-6e50991d2a Sat Aug 21 18:23:58 2021 +0000 darwin/amd64
golang-design/go2generics
Go 语言泛型的代码示例(基于类型参数和类型集)
实践一:简单的 Go 泛型代码
package main
import (
"fmt"
)
func Print[T any](s []T) {
for _, v := range s {
fmt.Print(v)
}
fmt.Println()
}
func main() {
Print([]string{"Hello, ", "Go generics"})
Print([]int{0, 1, 42})
Print([]int{0, 1, 42})
}
使用 go1.17 并附带编译参数 -gcflags=-G=3
,或者使用 gotip 执行结果如下:
$ go run -gcflags=-G=3 main.go
# command-line-arguments
./printer.go:7:6: internal compiler error: Cannot export a generic function (yet): Print
Please file a bug report including a short program that triggers the error.
https://golang.org/issue/new
$ gotip run main.go
Hello, Go generics
0142
0142
两者结果并不一致,这就说明 go 1.17 的泛型跟当前 master 的泛型支持相差还是很大的,不推荐大家用 go 1.17 使用 Go 泛型。
实践二:golang-design/go2generics
-
下载 golang-design/go2generics[3]
$ git clone https://github.com/golang-design/go2generics
...
-
进入 go2generics 目录
$ cd go2generics
$ gotip run demo/ex1-sort.go
sorted: [1 2 3]
sorted: [1 2 3]
$ gotip run demo/ex2-mapreduce.go
ret: [2 4 6 8 10 12 14 16 18 20]
ret: 55
$ gotip run demo/ex3-stack.go
$ gotip run demo/ex4-map.go
OK
$ cd errors && gotip test
PASS
ok golang.design/x/go2generics/errors 0.275s
$ cd fmt && gotip test
1
2
3
PASS
ok golang.design/x/go2generics/fmt 0.284s
$ cd future && gotip test
PASS
ok golang.design/x/go2generics/future 1.365s
$ cd linalg && gotip test
PASS
ok golang.design/x/go2generics/linalg 0.273s
$ cd list && gotip test
PASS
ok golang.design/x/go2generics/list 0.264s
$ cd math && gotip test
PASS
ok golang.design/x/go2generics/math 0.264s
$ cd metrics && gotip test
PASS
ok golang.design/x/go2generics/metrics 0.265s
$ cd ring && gotip test
PASS
ok golang.design/x/go2generics/ring 0.261s
$ cd stack && gotip test
PASS
ok golang.design/x/go2generics/stack 0.319s
$ cd strings && gotip test
PASS
ok golang.design/x/go2generics/strings 0.267s
$ cd sync/atomic && gotip test
PASS
ok golang.design/x/go2generics/sync/atomic 0.658s
已知问题
由于当前 Go 的编译器实现上不完整,目前(2021.08.22)已知这些问题:
-
泛型切片表达式尚未实现 -
公开函数的导出和包的导入还需要完善 -
更多类型检查相关的完善 -
更多满足语言规范的代码(暂时)还不能正常编译执行。
例如这些目录下的代码还不支持正常编译:
-
chans -
demo/ex5-loadbalance.go -
graph -
maps -
sched -
slices -
sync
进一步阅读(有关 Go 泛型的 Slide,视频)
-
Go 泛型:Constrained Type Parameters[4] -
Go 泛型:预览 [5] -
Go 泛型研究 [6] -
Go 夜读第 80 期:带你提前玩 Go 2 新特性:泛型 [7]
343732: cmd/compile: enable -G=3 by default | https://go-review.googlesource.com/c/go/+/343732 : https://go-review.googlesource.com/c/go/+/343732
[2]安装 gotip: https://pkg.go.dev/golang.org/dl/gotip
[3]golang-design/go2generics: https://github.com/golang-design/go2generics
[4]Go 泛型:Constrained Type Parameters: https://changkun.de/s/go2generics/
[5]go 泛型:预览: https://docs.google.com/presentation/d/1EG7e68ySeyOzkAmNy-G-h4ks632XZq3BNT7OyHoFpOk/edit?usp=sharing
[6]Go 泛型研究: https://github.com/golang-design/go2generics/blob/master/generics.md
[7]Go 夜读第 80 期:带你提前玩 Go 2 新特性:泛型: https://www.bilibili.com/video/BV1k7411R7ya/
点击阅读原文,直达 go2generics 项目
文章评论