什么是泛型
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
Golang 泛型基本用法
示例
map 操作
package main
import (
"fmt"
)
func mapFunc[T any, M any](a []T, f func(T) M) []M {
n := make([]M, len(a), cap(a))
for i, e := range a {
n[i] = f(e)
}
return n
}
func main() {
vi := []int{1, 2, 3, 4, 5, 6}
vs := mapFunc(vi, func(v int) string {
return "<" + fmt.Sprint(v * v) + ">"
})
fmt.Println(vs)
}
min max 函数
package main
import (
"fmt"
)
type ordered interface {
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string
}
func max[T ordered](a []T) T {
m := a[0]
for _, v := range a {
if m < v {
m = v
}
}
return m
}
func min[T ordered](a []T) T {
m := a[0]
for _, v := range a {
if m > v {
m = v
}
}
return m
}
func main() {
vi := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
result := max(vi)
fmt.Println(result)
vii := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
result2 := min(vii)
fmt.Println(result2)
}
Set
// Package sets implements sets of any comparable type.
package sets
// Set is a set of values.
type Set[T comparable] map[T]struct{}
// Make returns a set of some element type.
func Make[T comparable]() Set[T] {
return make(Set[T])
}
// Add adds v to the set s.
// If v is already in s this has no effect.
func (s Set[T]) Add(v T) {
s[v] = struct{}{}
}
// Delete removes v from the set s.
// If v is not in s this has no effect.
func (s Set[T]) Delete(v T) {
delete(s, v)
}
// Contains reports whether v is in s.
func (s Set[T]) Contains(v T) bool {
_, ok := s[v]
return ok
}
// Len reports the number of elements in s.
func (s Set[T]) Len() int {
return len(s)
}
// Iterate invokes f on each element of s.
// It's OK for f to call the Delete method.
func (s Set[T]) Iterate(f func(T)) {
for v := range s {
f(v)
}
}
尝鲜试用
Go2Goplaygroud: The go2go Playground
Go 1.17 (非最新,不推荐):
go run -gcflags=-G=3 myproject``/main``.go
go 1.18 (正式版已发布)
Golang 类型当前状况与泛型的意义
当前
interface
弊端:类型转换、缺乏编译时的约束
package main
import "fmt"
func printStr(x interface{}) {
value, ok := x.(string)
if !ok {
fmt.Println("It's not ok for type string")
return
}
fmt.Println("The value is ", value)
}
func main() {
printStr(123)
}
// It's not ok for type string
为不同类型单独编写
弊端:API与代码实现不整洁、工作量大等
示例:当前标准库的 sort, math 等
代码生成
弊端:需学习第三方代码生成工具、go:generate、AST等知识,不通用
示例:
https://github.com/cheekybits/genny
package gogenerate
import "github.com/cheekybits/genny/generic"
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
type KeyType generic.Type
type ValueType generic.Type
type KeyTypeValueTypeMap map[KeyType]ValueType
func NewKeyTypeValueTypeMap() map[KeyType]ValueType {
return make(map[KeyType]ValueType)
}
Go 官方泛型意义
- 通用操作与类型约束
- 函数式编程
- 简化标准库和第三方库的实现
………
Golang 泛型发展历史
简述 | 时间 | 作者 |
---|---|---|
[Type Functions] | 2010 年 | Ian Lance Taylor |
Generalized Types | 2011 年 | Ian Lance Taylor |
Generalized Types v2 | 2013 年 | Ian Lance Taylor |
Type Parameters | 2013 年 | Ian Lance Taylor |
go:generate | 2014 年 | Rob Pike |
First Class Types | 2015 年 | Bryan C.Mills |
Contracts | 2018 年 | Ian Lance Taylor, Robert Griesemer |
Contracts | 2019 年 | Ian Lance Taylor, Robert Griesemer |
Redundancy in Contracts(2019)’s Design | 2019 年 | Ian Lance Taylor, Robert Griesemer |
Constrained Type Parameters | 2020 年 | Ian Lance Taylor, Robert Griesemer |
Featherweight Go | 2020年 | Ian Lance Taylor, Robert Griesemer |
合并进 master、发布 Go 1.18、改进标准库 等 | 2021 - 2022 年 |
Golang 泛型实现原理
他山之石
不同语言泛型实现
boxing (装箱) VS monomorphization (单态化)
泛型的困境
https://research.swtch.com/generic
- 拖累程序员: 比如 C 语言,增加了程序员的负担,需要曲折的实现,但是不对增加语言的复杂性
- 拖累编译器: 比如 C++,增加了编译器的负担,可能会产生很多冗余的代码,重复的代码还需要编译器斟酌删除,编译的文件可能非常大
- 拖累执行时间: 比如 Java,将一些装箱成 Object, 进行类型擦除。虽然减少代码冗余、减少空间,但是需要装箱拆箱操作,代码效率低
Go
Keith H. Randal 的三个方案:
Generics implementation - Dictionaries
编译时实例化字典,字典包含为类型参数实例化的类型信息
Generics implementation - Stenciling
模板生成,为每个实例化类型生成一套独立的代码
Generics implementation - GC Shape Stenciling
混合实现,shape类型相同的类型,使用字典区分类型的不同行为,
类型的shape是指它对内存分配器/垃圾回收器呈现的方式,
包括大小、所需的对齐方式以及类型的哪些部分包含指针
参考资料
Go 官方和社区资料:
https://go.dev/doc/tutorial/generics
https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md
https://go.dev/blog/generics-proposal
https://go.dev/blog/generics-next-step
https://go.dev/blog/why-generics
https://github.com/golang/proposal/blob/master/design/generics-implementation-dictionaries.md
https://github.com/golang/proposal/blob/master/design/generics-implementation-stenciling.md
https://github.com/golang/proposal/blob/master/design/generics-implementation-gcshape.md
示例用法、文章分析等:
https://github.com/mattn/go-generics-example
https://colobu.com/2021/08/30/how-is-go-generic-implemented/
https://coolshell.cn/articles/21615.html
https://draveness.me/whys-the-design-go-generics/
https://taoshu.in/go/go-generics-design.html
https://research.swtch.com/generic
https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/