
Go 泛型用了半年,哪些场景真香,哪些是硬上
Go 1.18 引入泛型那会儿,我其实挺无感的。毕竟写 Go 这么多年,interface{} 加类型断言也活得挺好,何必多此一举?但半年下来,真香了。
先说最香的场景:写集合操作工具库。
以前想实现一个反转切片的功能,得为 int、string、float64 各写一个函数,名字还得起成 ReverseInts、ReverseStrings、ReverseFloats,贼难看。有了泛型之后,一行搞定:
func Reverse[T any](s []T) []T {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
说实话,第一次写出来的时候,感觉 Go 终于像个现代语言了。这个函数不管是传 []int 还是 []string 都能用,编译器自动推导类型,零运行时开销。比用反射硬搞的那种优雅太多了。
还有一个让人上头的场景:写并发任务池。
我们有个项目需要并发处理一批数据,结果类型不一样。以前的做法是传 chan interface{},然后在消费方做类型断言,跑起来心里老不踏实。用了泛型之后,直接定义成:
type TaskPool[T any] struct {
tasks chan Task[T]
results chan T
}
func (p *TaskPool[T]) Run(workers int, fn func(T) Result) []Result { ... }
类型安全拉满,IDE 提示也正常了。同事 review 代码的时候说了一句:"这代码看着就舒服。" 我心想,值了。
但也有翻车的时候。
说实话,泛型在 Go 里不是万能的。跟 interface 混用的时候,类型约束写起来真的很痛苦。比如你想约束一个类型既要有 Compare 方法又要能序列化——好家伙,得写嵌套约束,那语法看得人脑壳疼。
再就是编译速度。我们项目里把一些常用的泛型工具抽成了单独的包,结果每次改代码重新编译,明显比之前慢了。尤其是用了 constraints.Ordered 这种内置约束的时候,编译时间直接翻倍。
我的建议是:不要为了用泛型而用泛型。
如果你的函数里就一个类型参数,而且只需要写一个版本——那还不如直接写具体类型。泛型最佳的应用场景是:相同逻辑要处理 N 种不同类型,而且这些类型之间确实有共同的行为约束。满足这个条件,上泛型绝对不亏。
半年用下来,泛型给我最大的感受是:Go 团队没把这事搞砸。虽然不像 Rust 的 trait 或者 Java 的泛型那么强大,但在 Go 的哲学范围内做到了恰到好处。可以期待一下后续版本对泛型性能的进一步优化。
你现在项目里用泛型了吗?来评论区聊聊你的踩坑经历。
评论 (0)
暂无评论,来写第一条吧 ✍️