一、泛型的约束体系
Go 1.18引入的泛型通过接口作为约束(Constraint)来限定类型参数的能力范围。正确使用约束是写出高质量泛型代码的关键。
1.1 约束接口的演进
// Go 1.18前的约束方式(类型断言 + interface{}):
// func min(a, b interface{}) interface{} {
// av := a.(int) // 需要类型断言,容易panic
// bv := b.(int)
// if av < bv { return av }
// return bv
// }
// Go 1.18+ 泛型约束:
// ① 基本约束(Comparable)
func contains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target { // comparable保证==/!=可用
return true
}
}
return false
}
// ② Ordered约束(支持比较运算符)
func min[T ordered](a, b T) T {
if a < b { return a }
return b
}
// ③ 自定义约束(interface + 类型列表)
type Integer interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64
}
func sum[T Integer](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
// ~int 表示底层类型为int的所有类型(包括自定义别名)
type MyInt int
func test() {
s := sum([]MyInt{1, 2, 3}) // MyInt底层是int,可参与sum
}
二、实用泛型设计模式
2.1 泛型Repository模式
// 传统写法:每个Entity重复相似的CRUD代码
type User struct { ID int64; Name string }
type Product struct { ID int64; Name string; Price float64 }
// 泛型版本:
type Entity interface {
GetID() int64
}
type Repository[T Entity] struct {
db *sqlx.DB
mu sync.RWMutex
mem map[int64]T
}
func NewRepository[T Entity](db *sqlx.DB) *Repository[T] {
return &Repository[T]{
db: db,
mem: make(map[int64]T),
}
}
func (r *Repository[T]) Create(ctx context.Context, entity T) error {
r.mu.Lock()
defer r.mu.Unlock()
r.mem[entity.GetID()] = entity
return nil
}
func (r *Repository[T]) GetByID(ctx context.Context, id int64) (T, error) {
r.mu.RLock()
defer r.mu.RUnlock()
if entity, ok := r.mem[id]; ok {
return entity, nil
}
var zero T
return zero, ErrNotFound
}
// 使用:
type UserEntity struct { ID int64; Name string }
func (u UserEntity) GetID() int64 { return u.ID }
repo := NewRepository[UserEntity](db)
user, err := repo.GetByID(ctx, 1)
2.2 泛型Pipeline模式
// Pipeline:函数式数据处理
type Pipeline[T any] struct {
data []T
}
func of[T any](items ...T) *Pipeline[T] {
return &Pipeline[T]{data: items}
}
func (p *Pipeline[T]) Map[R any](fn func(T) R) *Pipeline[R] {
result := make([]R, len(p.data))
for i, v := range p.data {
result[i] = fn(v)
}
return &Pipeline[R]{data: result}
}
func (p *Pipeline[T]) Filter(fn func(T) bool) *Pipeline[T] {
result := make([]T, 0)
for _, v := range p.data {
if fn(v) {
result = append(result, v)
}
}
return &Pipeline[T]{data: result}
}
func (p *Pipeline[T]) Collect() []T {
return p.data
}
// 使用示例(函数式链式调用):
numbers := of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).
Filter(func(n int) bool { return n%2 == 0 }).
Map(func(n int) int { return n * n }).
Map(func(n int) string { return fmt.Sprintf("val=%d", n) }).
Collect()
// []string{"val=4", "val=16", "val=36", "val=64", "val=100"}
2.3 泛型错误处理模式
// Go泛型Result类型(类似Rust的Result)
type Result[T any] struct {
value T
err error
}
func Ok[T any](value T) Result[T] {
return Result[T]{value: value, err: nil}
}
func Err[T any](err error) Result[T] {
return Result[T]{err: err}
}
func (r Result[T]) Unwrap() (T, error) {
return r.value, r.err
}
func (r Result[T]) MustUnwrap() T {
if r.err != nil {
panic(r.err)
}
return r.value
}
// 使用:
func parseInt(s string) Result[int] {
v, err := strconv.Atoi(s)
if err != nil {
return Err[int](err)
}
return Ok(v)
}
// 链式错误处理:
result := parseInt("42").
Map(func(n int) int { return n * 2 }).
Map(func(n int) int { return n + 1 })
value, err := result.Unwrap()
// value = 85, err = nil
三、泛型的性能与边界
// 泛型的编译时展开(Monomorphization)
// Go编译器为每个类型组合生成专用代码
// 泛型函数泛型本体 → 针对T=int生成分支 → 针对T=string生成分支
// benchmark对比:泛型 vs interface{}
func genericMin[T ordered](a, b T) T {
if a < b { return a }
return b
}
func interfaceMin(a, b interface{}) interface{} {
av := a.(int)
bv := b.(int)
if av < bv { return av }
return bv
}
// Benchmark结果:
// BenchmarkGeneric-8 1000000000 0.52 ns/op
// BenchmarkInterface-8 100000000 12.4 ns/op
// 泛型快24倍!(interface需要类型断言+装箱拆箱)
// ⚠️ 泛型的局限性:
// ① 不能约束方法的具体实现
type DataProcessor[T any] interface {
Process(T) error // 只能约束方法签名,不能提供默认实现
}
// ② 不能泛型方法(只能泛型函数或泛型类型)
// ❌ 不合法:
func (t T) process(item T) { } // 方法不能带类型参数
// ✅ 合法:类型本身泛型
type Processor[T any] struct {
fn func(T) T
}
func (p Processor[T]) process(item T) T {
return p.fn(item)
}