go 1.18 release note 中对于泛型提到了语法上的 6 条改变1:
- The syntax for function and type declarations now accepts type parameters.
- Parameterized functions and types can be instantiated by following them with a list of type arguments in square brackets.
- The new token
~
has been added to the set of operators and punctuation.- The syntax for Interface types now permits the embedding of arbitrary types (not just type names of interfaces) as well as union and
~T
type elements. Such interfaces may only be used as type constraints. An interface now defines a set of types as well as a set of methods.- The new predeclared identifier
any
is an alias for the empty interface. It may be used instead ofinterface{}
.- The new predeclared identifier
comparable
is an interface that denotes the set of all types which can be compared using==
or!=
. It may only be used as (or embedded in) a type constraint.
我们或许可以把他们总结为 3 大类:
- 在 function 中可以使用泛型
- 在 type 中可以使用泛型
- 在 interface 中可以使用泛型
我们逐一讨论。
Go 文档的一个泛型语法教程2里展示了在定义 function 时使用泛型的案例。比如:
1 | // SumIntsOrFloats sums the values of map m. It supports both int64 and float64 |
如此,SumIntsOrFloats
就可以处理多种类型的参数。但如果没有泛型的语法,是否可以用一个 function 处理多种类型呢。go 中的 interface 或许支持这种想法,比如:
1 | func SumIntsOrFloatsWithInterface(m interface{}) interface{} { |
又或者把不同类型的 map 抽象出一个带有 Sum() 方法的接口,比如:
1 | type NumMap interface { |
但是不使用泛型的写法
- 看起来有点啰嗦
- 返回值的类型可能还需要再 type assertion 或者 switch 一下
- 类型错误可能会在 runtime 中发生,而泛型的参数类型如果不被支持,在 compile time 就会报错2
综上,虽然我不知道什么时候使用泛型函数合适,但是所有需要 type switch 的地方,或许都可以考虑一下泛型的写法。
声明 type 时也可以使用类型参数,从而构成泛型。比如 go 文档中的一个例子 3:
1 | type List[T any] struct { |
这样的泛型 type 可以实例化、用于指定函数的参数类型、甚至作为 receiver 去声明 methods:
1 | // 1. 泛型 type 的实例化 |
如此使用泛型也可以避免写极其相似的代码来构建属性、方法都接近的 types。
最后,在 interface 中可以插入非 interface 的类型,形成一个 type constraint,但这样的 interface 也只能作为 type constraint 了。在目前的 go 语法规则下,它不能实例化,不能作为函数的参数类型或者 composite 结构的 field 类型。
1 | type Interface interface { |
但是这样的 type constraint 依然可以插入接口需要的 method 或者其他 interface。比如下面的语句是可以的:
1 | type Interface interface { |
有博客4指出,go 文档将 interface 的定义从 a method set 改为了 a type set。在 type constraint 中混合 interface 原先的用法,达成的效果是进一步限制 type constraint 的范围5,实现一个不会比之前的 type constraint 更大的 type set。
而以前 method set 形式的 interface 现在是一个没有 type union(type constraint 中类似 int | float64
的语句)的 type set,除了作他以前的用途(比如,声明后实例化为一个 nil 的变量,指定函数的参数类型)也可以用作 type constraint,比如:
1 | func test[A NumMap](a A) { |
综上,我们对比一下含有和不含有 type union 的 interface 的区别:
含有 type union 的 interface | 不含有 | |
---|---|---|
实例化 | 不可以 | 可以声明出一个 nil 的变量 |
指定参数类型或 filed 类型 | 不可以 | 可以 |
嵌入其他 interface 的效果 | 限制 type set | 继承接口的 methods |
必须作为 type constraint 使用 | 是 | 不是 |
1. “Go 1.18 Release Notes - The Go Programming Language.” 1 Aug. 2022, go.dev/doc/go1.18#generics. ↩
2. “Tutorial: Getting started with generics - The Go Programming Language.” 1 Aug. 2022, go.dev/doc/tutorial/generics. ↩
3. “The Go Programming Language Specification - The Go Programming Language.” 1 Aug. 2022, go.dev/ref/spec#Type_parameter_declarations. ↩
4. “Go generic programming: interface is no longer the interface.” 9 Jan. 2022, www.sobyte.net/post/2022-01/the-interface-is-not-that-interface-in-go-1-18. ↩
5. “Three new concepts related to interfaces since Go 1.18.” 18 Jan. 2022, www.sobyte.net/post/2022-01/three-new-concepts-of-go-interface-since-1-18. ↩