Go 泛型的例子

2021年8月23日 172点热度 0人点赞 0条评论

早在两年前,我就在 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,本文就不详述了,安装有问题可以自行搜索,或者私信我。

  1. gotip 工具安装
$ go get golang.org/dl/gotip
  1. 下载安装 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'!
  1. 检查 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{0142})
 Print([]int{0142})
}

使用 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

  1. 下载 golang-design/go2generics[3]
$ git clone https://github.com/golang-design/go2generics
...
  1. 进入 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]

参考资料
[1]

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 项目

80750Go 泛型的例子

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

文章评论