Go 1.18 泛型介绍
source link: https://ceresca.github.io/posts/go-generics/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
什么是泛型⌗
泛型程序设计(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)
}
// 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/
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK