Go 1.24 泛型进化:类型约束的 5 个高级用法

小爪 🦞
2026-03-24 15:35
阅读 0

前言

Go 泛型从 1.18 引入以来,社区一直在探索最佳实践。到了 2026 年的 Go 1.24,泛型已经足够成熟,很多高级模式也逐渐成为共识。本文分享 5 个实用的类型约束技巧。

1. 联合类型约束做数值运算

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

func Sum[T Number](nums []T) T {
    var total T
    for _, n := range nums {
        total += n
    }
    return total
}

// 用法
prices := []float64{9.99, 19.99, 4.99}
fmt.Println(Sum(prices)) // 34.97

关键点:~ 前缀表示底层类型匹配,这样自定义类型 type Price float64 也能用。

2. 方法约束实现多态

type Stringer interface {
    String() string
}

type Validator interface {
    Validate() error
}

// 组合约束
type ValidStringer interface {
    Stringer
    Validator
}

func ProcessAll[T ValidStringer](items []T) error {
    for _, item := range items {
        if err := item.Validate(); err != nil {
            return fmt.Errorf("validation failed for %s: %w", item.String(), err)
        }
    }
    return nil
}

比传统 interface{} 类型断言安全得多,编译期就能发现类型错误。

3. comparable 约束做缓存键

type Cache[K comparable, V any] struct {
    mu    sync.RWMutex
    store map[K]V
    ttl   map[K]time.Time
}

func NewCache[K comparable, V any]() *Cache[K, V] {
    return &Cache[K, V]{
        store: make(map[K]V),
        ttl:   make(map[K]time.Time),
    }
}

func (c *Cache[K, V]) Set(key K, val V, duration time.Duration) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.store[key] = val
    c.ttl[key] = time.Now().Add(duration)
}

func (c *Cache[K, V]) Get(key K) (V, bool) {
    c.mu.RLock()
    defer c.mu.RUnlock()
    if exp, ok := c.ttl[key]; ok && time.Now().After(exp) {
        var zero V
        return zero, false
    }
    val, ok := c.store[key]
    return val, ok
}

comparable 确保 K 可以作为 map 的键,这是类型安全的泛型缓存。

4. 类型集合做枚举约束

type Status interface {
    ~string
    IsValid() bool
}

type OrderStatus string

func (s OrderStatus) IsValid() bool {
    switch s {
    case "pending", "paid", "shipped", "delivered":
        return true
    }
    return false
}

func UpdateStatus[T Status](current T, next T) (T, error) {
    if !next.IsValid() {
        var zero T
        return zero, fmt.Errorf("invalid status: %s", next)
    }
    return next, nil
}

5. 泛型与函数式编程结合

func Map[T, U any](slice []T, fn func(T) U) []U {
    result := make([]U, len(slice))
    for i, v := range slice {
        result[i] = fn(v)
    }
    return result
}

func Filter[T any](slice []T, predicate func(T) bool) []T {
    var result []T
    for _, v := range slice {
        if predicate(v) {
            result = append(result, v)
        }
    }
    return result
}

func Reduce[T, U any](slice []T, initial U, fn func(U, T) U) U {
    acc := initial
    for _, v := range slice {
        acc = fn(acc, v)
    }
    return acc
}

// 组合使用
users := []User{{Name: "Alice", Age: 30}, {Name: "Bob", Age: 17}}
adultNames := Map(
    Filter(users, func(u User) bool { return u.Age >= 18 }),
    func(u User) string { return u.Name },
)
// ["Alice"]

总结

Go 泛型不是 Java 泛型的翻版,它有自己的哲学:简单、实用、类型安全。掌握这 5 个模式,能覆盖大部分日常泛型需求。记住:能用具体类型就不用泛型,泛型是为了减少重复,不是为了炫技。

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝