Go 泛型用了半年,哪些场景真香,哪些是硬上
技术笔记 0 次阅读

Go 泛型用了半年,哪些场景真香,哪些是硬上

Go 泛型封面

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)

评论通过后显示

暂无评论,来写第一条吧 ✍️