泛型
翻译整理自 Tutorial: Getting started with generics 与 Generics Proposal。
Go 1.18 引入泛型(Generics),让函数和类型可以接受类型参数,在编译期生成类型安全的代码。
1. 类型参数
func Print[T any](s []T) {
for _, v := range s {
fmt.Println(v)
}
}
Print[int]([]int{1, 2, 3}) // 显式指定
Print([]string{"a", "b"}) // 推断[T any] 中 any 是约束(constraint),等价于 interface{}。
2. 类型约束:comparable
需要比较相等用 comparable:
func Index[T comparable](s []T, x T) int {
for i, v := range s {
if v == x {
return i
}
}
return -1
}comparable 是预声明的约束,表示类型支持 == 与 !=。
3. 自定义约束
golang.org/x/exp/constraints(或自己定义):
type Number interface {
int | int8 | int16 | int32 | int64 |
float32 | float64
}
func Sum[T Number](nums []T) T {
var sum T
for _, v := range nums {
sum += v
}
return sum
}
Sum([]int{1, 2, 3}) // 6
Sum([]float64{1.5, 2.5}) // 4.0~T 表示底层类型
type Number interface {
~int | ~float64
}
type MyInt int
var x MyInt = 10
// Sum([]MyInt{x}) 也能编译,因为有 ~4. 泛型类型
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(v T) {
s.items = append(s.items, v)
}
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if len(s.items) == 0 {
return zero, false
}
v := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return v, true
}5. 多类型参数
func Map[T, U any](in []T, f func(T) U) []U {
out := make([]U, len(in))
for i, v := range in {
out[i] = f(v)
}
return out
}
doubled := Map([]int{1, 2, 3}, func(v int) int { return v * 2 })
strs := Map([]int{1, 2}, strconv.Itoa)6. 约束组合与嵌入
type Stringer interface {
String() string
}
type Constraint interface {
Number
Stringer
}7. 标准库泛型包
Go 1.21+ 提供以下泛型容器包:
slices:泛型切片操作(Sort、Contains、Reverse)maps:泛型 map 操作(Copy、Keys、Values)golang.org/x/exp:扩展包,包含constraints、slices早期版本
s := []int{3, 1, 2}
slices.Sort(s)
fmt.Println(slices.Contains(s, 1))8. 使用建议
- 优先具体类型,必要时再用泛型。Go 强调简洁,泛型不是银弹。
- 用于通用数据结构(栈、队列、树、map 工具)最有价值。
- 不要把泛型当反射用,性能与可读性都不一定更好。
- 在
go vet中检查泛型代码是否符合约束。
小结
泛型让 Go 在保留简洁的同时获得了类型安全的复用能力。建议先用、再优化,把它当作可选工具。