Go 泛型约束:从 comparable 到自定义类型集

泛型让容器与算法复用更干净,但约束写多了,API 会开始「吵」。少即是多。

自 Go 1.18 起,类型参数成为语言一部分。日常最常用的内建约束是 anycomparable;更细的需求则用接口定义类型集。

类型集,不是「方法列表」那么简单

约束接口既可以列出方法,也可以用元素类型枚举允许的底层类型:

type Number interface {
    ~int | ~int64 | ~float64
}

func Sum[T Number](xs []T) T {
    var s T
    for _, v := range xs {
        s += v
    }
    return s
}

~ 表示包含以该类型为底层类型的自定义类型,这在包装基础类型时很关键。

comparable 的适用面

map 的键、相等比较,需要 comparable。注意:切片、函数、含切片字段的结构体等不可比。若你的泛型 API 依赖 ==,约束里写清楚,比运行期 panic 更友好。

设计经验

  • 先接口后泛型:若行为多态足够,不必上类型参数。
  • 约束贴近操作:只用到 + 就别引入一堆无关方法。
  • 避免过度抽象:为两个调用点写三层泛型工具包,往往得不偿失。
  • 导出约束要克制:包内约束可以细;对外 API 宁可简单。

小结

泛型是消除重复的手术刀,不是展示类型体操的舞台。约束写到「刚好能编译通过你需要的操作」即可,剩下的留给清晰的命名与测试。