大家好,我是煎鱼。
在 Go1.18 版本发布了泛型,带来了:类型参数(type parameters)、类型约束(type constraints)、核心类型(core types)等一大堆泛型带出来的新概念。引起了一顿新知识的的学习浪潮。
图片
最早泛型 2019 年到现在 2025 年差不多也有个 6 年了。
背景
在 Go1.25 的新特性提前宣发中,Go 核心团队正式发布《Goodbye core types - Hello Go as we know and love it![1]》,要将泛型中的核心类型(core types)给干掉。
图片
要被毕业的核心类型,又到底是什么呢?
核心类型(Core types)
在 Go 1.18 中,我们引入了 “核心类型[2]”(core type)的概念,以便更容易检查类型参数的某些约束。
我们先放例子,我觉得例子比起纯理论可能更容易理解。
在 Go 官方的《go spec》中,具体包含核心类型的例子:
注意:右侧注释就是他所标注的核心类型。
不包含核心类型的例子:
核心类型的定义如下:
- 如果某类型 T 的定义是一个类型别名,例如
type T = A
,那么 T 的核心类型就是 A 的核心类型。 - 如果某类型 T 是一个底层类型为某个具体类型的命名类型,那么它的核心类型就是那个底层类型。
- 如果某类型 T 是一个类型参数,并且其约束指定了一个核心类型(比如
~int
或~[]byte
),那么它的核心类型就是那个指定的类型。 - 否则,该类型没有核心类型。
为什么不要核心类型了?
虽然引入了核心类型试图统一泛型语义。
结果却让语言规范更复杂、学习门槛更高、规则更难一致,发现最终得不偿失。
具体原因如下:
- 核心类型定义不够灵活,限制过严:
比如接口 interface{ ~[]int }
拥有核心类型 []int
,但更复杂的接口如 Constraint
(即含有方法和多个类型约束的接口)就无法推导出核心类型。
对于 append
、copy
、通道操作等语言特性,核心类型定义过于严格,实际需要对方向、类型组合做额外处理(例如同时包含 []byte
和 string
)。
- 对语言特性理解造成阻碍:
- 某些语言特性(如切片表达式)在规范中以“核心类型”为判断依据,而非更直接地说明“操作数必须是数组、切片或字符串”。
- 这让开发者即使写的不是泛型代码,也得理解 “核心类型” 的概念,徒增学习负担。
- 破坏语言规则的一致性,导致了例外:
- 有些表达式(如
len
,cap
, 索引表达式等)并不依赖核心类型判断,导致语言规则看起来像是有一堆例外。 - 例如社区提案 #48522[3] 中提到的 “对联合类型元素字段的统一访问”,如果没有核心类型这个障碍,其实可以自然落入普通字段访问规则中,而不是再新增例外。
Go1.25 会做出什么改变?
由于上述提到的一系列导致 Go 更复杂的问题,Go 核心团队将决定在 Go1.25(2025 年 8 月)的语言规范中将核心类型的概念给移除掉。并给未来预留有需要时将其概念转为显式的方式。
#go/issues/70128
#go/issues/70128
移除后,将有以下几个好处:
- 语言规范更简洁,易于学习:
Go 规范呈现的概念更少,不再引入“核心类型”这种额外抽象。
初学者能更快理解语言的核心结构,不被泛型细节干扰。
- 非泛型代码无需了解泛型语义:
- 你写的是普通代码,就不需要去理解类型参数、类型约束或核心类型。
- 泛型不再“污染”对普通语言特性的理解,使 Go 保持其一贯的简洁直观。
- 针对具体操作采用具体规则,更具灵活性:
- 用 “具体规则匹配具体操作” 而非 “一刀切” 的抽象模型,为语言扩展打开了大门。
- 不仅让 issue #48522(字段选择器适用于类型集合)更自然,也为将来支持更强大的切片操作、更智能的类型推导等功能提供空间。
总结
Go 泛型虽然已经发布了 6 年,但目前仍然在持续的迭代过程中。在以往包含核心类型的概念时,泛型确实有一定的学习成本。
在后续的 Go1.25 新版本移除后,虽然有所降低。但是感觉泛型的更好的成熟可用还是有一定的发展空间的。
参考资料
[1] Goodbye core types - Hello Go as we know and love it!: https://go.dev/blog/coretypes
[2] 核心类型: https://go.dev/ref/spec#Core_types
[3] #48522: https://go.dev/issue/48522