不支持

Feature Desc
泛型特化 编写一个泛型函数针对某个具体类型的特殊版本
元编程 编写在编译时执行的代码来生成运行时执行的代码
操作符方法 不能将操作符视为方法并自定义其实现
变长类型参数

容器

重复代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func maxInt(sl []int) int {
if len(sl) == 0 {
panic("empty slice")
}

max := sl[0]
for _, v := range sl[1:] {
if v > max {
max = v
}
}
return max
}

func maxString(sl []string) string {
if len(sl) == 0 {
panic("empty slice")
}

max := sl[0]
for _, v := range sl[1:] {
if v > max {
max = v
}
}
return max
}

any

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func maxAny(sl []any) any {
if len(sl) == 0 {
panic("empty slice")
}

max := sl[0]
for _, v := range sl[1:] {
switch v.(type) { // type switch
case int:
if v.(int) > max.(int) { // type assertion
max = v
}
case string:
if v.(string) > max.(string) {
max = v
}
}
}
return max
}
builtin/builtin.go
1
2
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

缺陷

  1. 基于 type switch,若要支持其它元素类型,需要修改代码,不符合开闭原则
  2. 返回值为 any,调用者使用实际类型的值还需要通过 type assertion 转换
  3. any 作为入参和返回值的元素类型,存在装箱拆箱操作,存在性能损耗
1
2
3
4
5
6
7
8
9
10
11
12
13
func BenchmarkMaxInt(b *testing.B) {
sl := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
for i := 0; i < b.N; i++ {
maxInt(sl)
}
}

func BenchmarkMaxAny(b *testing.B) {
sl := []any{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
for i := 0; i < b.N; i++ {
maxAny(sl)
}
}
1
2
3
4
5
6
7
8
9
10
$ go test -v -bench .
goos: darwin
goarch: arm64
pkg: github.com/xxx/gc
BenchmarkMaxInt
BenchmarkMaxInt-10 296372949 4.052 ns/op
BenchmarkMaxAny
BenchmarkMaxAny-10 120496152 9.954 ns/op
PASS
ok github.com/xxx/gc 3.822s

泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}

func maxGenerics[T ordered](sl []T) T {
if len(sl) == 0 {
panic("empty slice")
}

max := sl[0]
for _, v := range sl[1:] {
if v > max {
max = v
}
}
return max
}

type myString string

func main() {
m := maxGenerics([]int{1, 2, 3, 4, 5})
fmt.Printf("%T %v\n", m, m) // int 5
fmt.Println(maxGenerics([]int8{1, 2, 3, 4, 5})) // 5
fmt.Println(maxGenerics([]myString{"11", "22", "44", "66", "77", "10"})) // 77
fmt.Println(maxGenerics([]float64{1.1, 2.2, 3.3, 4.4, 5.5})) // 5.5
}

性能要优于 any 版本

1
2
3
4
5
6
7
8
9
10
11
12
$ go test -v -bench .
goos: darwin
goarch: arm64
pkg: github.com/xxx/gc
BenchmarkMaxInt
BenchmarkMaxInt-10 293859739 4.052 ns/op
BenchmarkMaxAny
BenchmarkMaxAny-10 120446469 9.958 ns/op
BenchmarkMaxGenerics
BenchmarkMaxGenerics-10 214360436 5.602 ns/op
PASS
ok github.com/xxx/gc 5.585s

类型参数

Go 泛型方案的实质是对类型参数的支持

Type Desc
泛型函数 - generic function 带有类型参数的函数
泛型类型 - generic type 带有类型参数的自定义类型
泛型方法 - generic method 泛型类型绑定的的方法

泛型函数

[T ordered] 为 Go 泛型的类型参数列表
T 为类型参数,而 ordered 为类型参数类型约束(type constraint

1
2
3
func maxGenerics[T ordered](sl []T) T {
// ...
}
1
func genericsFunc[T1 constraint1, ...Tn constraintN](ordinary parameters list) (return values list)
  1. 函数一旦拥有类型参数,可以用该参数作为常规参数列表和返回值列表中修饰参数返回值的类型
  2. 类型参数的名字首字母通常为大写,并且参数类型必须具名
  3. 同一个类型参数列表中,类型参数的名字唯一
1
2
3
4
5
func print[T any]() {} // ok

func print[any]() {} // syntax error: missing type constraint

func compare[T1 any, T1 comparable]() {} // T1 redeclared in this block

作用域(类型参数的声明顺序不会影响泛型函数的行为)

image-20231204001928657

调用

  1. 类型参数分为类型形参(type parameter)和类型实参(type argument
  2. 泛型函数中声明的类型参数为类型形参,实际调用泛型函数时传递(或者编译器自动推导)的类型为类型实参
1
2
3
4
5
6
7
8
9
// 声明泛型函数:T 为类型形参
func maxGenerics[T ordered](sl []T) T

// 调用泛型函数:int8 为类型实参 - 显式传递
maxGenerics[int8]([]int8{1, 2, 3, 4, 5})

// 调用泛型函数:int 为类型实参 - 编译器自动推导
// 通过函数实参的类型来推断类型实参的类型
maxGenerics([]int{1, 2, 3, 4, 5})

image-20231204173823411

自动推断的前提:必须在函数的参数列表中使用了类型形参

1
2
3
4
5
6
7
func foo[T comparable, E any](a int, s E) {}

func main() {
foo[int](5, "hello") // ok
foo[int, string](5, "hello") // ok
foo(5, "hello") // cannot infer T
}

无法通过返回值类型来推断类型实参

1
2
3
4
5
6
7
8
9
func foo[T any]() T {
var t T
return t
}

func main() {
var a int = foo() // cannot infer T
println(a)
}

调用过程

1
maxGenerics([]int{1, 2, -4, -6, 7, 0})

image-20231206220538145

  1. Go 对泛型函数进行实例化(Instantiation)
    • 根据自动推断出的类型实参生成一个新函数
    • 编译阶段完成,不会影响运行时性能
  2. 然后调用新函数对输入的函数参数进行处理
1
2
maxGenericsInt := maxGenerics[int]                                               // func([]int) int
fmt.Printf("%T %v\n", maxGenericsInt, maxGenericsInt([]int{1, 2, -4, -6, 7, 0})) // func([]int) int 7

使用相同类型实参对泛型函数进行调用,仅会实例化一次

泛型类型

类型声明中带有类型参数的 Go 类型,一般形式如下

1
type TypeName[T1 constraint1, ...Tn constraintN] TypeLiteral
1
2
3
4
5
6
7
8
9
10
type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}

type maximizableSlice[T ordered] struct {
elems []T
}

类型参数的作用域

image-20231208214912652

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
type Set[T comparable] map[T]struct{}

type sliceFn[T any] struct {
s []T
cmp func(T, T) bool
}

type node[K, V any] struct{}

type Map[K, V any] struct {
root *node[K, V]
compare func(K, K) int
}

type element[T any] struct {
next *element[T]
val T
}

type Number interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~complex64 | ~complex128
}

type NumericAbs[T Number] interface {
Abs() T
}

泛型类型内部使用自身,注意顺序

1
2
3
4
5
6
7
type P[T1, T2 any] struct {
F *P[T1, T2] // OK
}

type P[T1, T2 any] struct {
F *P[T2, T1] // 不符合泛型技术方案,但编译器并未报错
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import "fmt"

type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}

type maximizableSlice[T ordered] struct {
elems []T
}

func main() {
sl := maximizableSlice[int]{
elems: []int{1, 2, 3, 4, 5},
}
fmt.Printf("%T\n", sl) // main.maximizableSlice[int]
}

实例化(Instantiation),根据传入的类型实参(int)生成一个新类型,并创建该类型的实例

1
2
3
type maximizableIntSlice struct {
elems []int
}

泛型类型尚支持自行推断

1
2
3
sl := maximizableSlice{ // cannot use generic type maximizableSlice[T ordered] without instantiation
elems: []int{1, 2, 3, 4, 5},
}

类型别名

类型别名与其绑定的原类型完全等价,但仅限于原类型是一个直接类型,而非泛型类型

1
2
3
4
5
6
7
8
type foo[T1 any, T2 comparable] struct {
a T1
b T2
}

type coo = foo[int, int] // ok,已经完成实例化

type bar = foo // cannot use generic type foo[T1 any, T2 comparable] without instantiation

泛型类型只有在完成实例化后,才能用于声明变量,才能成为一个直接类型,才能被用于类型别名

1
2
3
4
type fooInstantiation struct {
a int
b int
}

类型嵌入

可以在泛型类型定义中嵌入普通类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Lockable[T any] struct {
t T
sync.Mutex
}

func (l *Lockable[T]) Get() T {
defer l.Unlock()

l.Lock()
return l.t
}

func (l *Lockable[T]) Set(v T) {
defer l.Unlock()

l.Lock()
l.t = v
}

可以将其它泛型类型实例化后的类型作为成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type Slice[T any] []T

func (s Slice[T]) String() string {
if len(s) == 0 {
return ""
}

var result = fmt.Sprintf("%v", s[0])
for _, v := range s[1:] {
result = fmt.Sprintf("%v, %v", result, v)
}
return result
}

type Lockable[T any] struct {
sync.Mutex // type embedding
Slice[int] // type embedding + generic type instantiation

t T
}

func main() {
n := Lockable[string]{
t: "hello",
Slice: []int{1, 2, 3}, // 使用泛型类型名作为嵌入后的字段名
}
fmt.Println(n.String()) // 1, 2, 3
}

在普通类型定义中,可以使用实例化后的泛型类型作为成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type Slice[T any] []T

func (s Slice[T]) String() string {
if len(s) == 0 {
return ""
}

var result = fmt.Sprintf("%v", s[0])
for _, v := range s[1:] {
result = fmt.Sprintf("%v, %v", result, v)
}
return result
}

type Foo struct {
Slice[int] // type embedding + generic type instantiation
}

func main() {
f := Foo{
Slice[int]{1, 2, 3, 4, 5},
}
fmt.Println(f.String()) // 1, 2, 3, 4, 5
}

类型嵌入类型别名类似,仅限于实例化后的泛型类型(成为直接类型,可以直接用于声明变量

泛型方法

泛型类型定义的方法称为泛型方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}

type maximizableSlice[T ordered] struct {
elems []T
}

func (sl *maximizableSlice[T]) max() T {
if len(sl.elems) == 0 {
panic("slice is empty")
}

max := sl.elems[0]
for _, elem := range sl.elems[1:] {
if elem > max {
max = elem
}
}
return max
}

泛型方法自身不支持类型参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 |
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
~float32 | ~float64 |
~string
}

type maximizableSlice[T ordered] struct {
elems []T
}

func (sl *maximizableSlice[T]) max[E any](e E) T { // syntax error: method must have no type parameters
if len(sl.elems) == 0 {
panic("slice is empty")
}

max := sl.elems[0]
for _, elem := range sl.elems[1:] {
if elem > max {
max = elem
}
}
return max
}

在泛型方法中,receiver 的某个类型参数如果没有使用,可以标记为 _

1
2
3
4
5
6
7
8
9
10
11
type foo[A comparable, B any] struct{}

func (foo[A, B]) M1() {} // ok

func (foo[A, _]) M2() {} // ok

func (foo[_, B]) M3() {} // ok

func (foo[_, _]) M4() {} // ok

func (foo[]) M5() {} // syntax error: unexpected ), expecting type

泛型方法中的 receiver 类型参数名字可以与泛型类型中的类型形参名不同,位置数量对齐即可

1
2
3
type foo[A comparable, B any] struct{}

func (foo[First, Second]) M1(a First, b Second) {} // ok