Go 1.18 关于泛型特性的说明

../_images/flying-gopher.png

Note

本文由黄健宏翻译自 《Go 1.18 Release Notes》

Go 1.18 实现了类型参数提案中描述的泛型特性。 这对 Go 语言来说是一个非常大的改动,但这些改动是完全向后兼容的。

为了检验泛型对语言所作的修改,我们需要大量崭新的、经过良好测试的生产环境代码。 然而由于现在使用泛型特性的人还不够多,我们暂时还无法做到这一点。 我们相信泛型特性实现得很好,质量也很高,但是跟 Go 的其他绝大部分不一样,这一特性尚未经过现实世界验证。 因此我们鼓励你在有意义的地方使用泛型,但是如果你想要把泛型代码部署到生产环境,那么请务必小心谨慎进行。

尽管我们相信新的语言特性设计得非常好,并且说明得也非常清楚,但我们还是有可能会犯错误。 正如我们在Go 1 兼容性保证中所说:“为了解决规范中的不一致或不完整,我们保留包括更新实现在内的解决问题的权利,即便这一行为可能会影响现有程序的意义或合法性”,以及:“如果编译器或者库的某个 bug 违反了规范,那么当这个 bug 被修复时,依赖该 bug 的程序可能会被破坏,但我们保留修复这种 bug 的权利”。

换句话说,一些在 1.18 中能够使用的泛型代码可能会在之后的版本中无法使用。 这并非我们有意为之,而是由于某些我们今天无法预见的原因,我们可能会在未来的版本中不得不破坏 1.18 版本的程序。 我们会尽可能减少这种破坏,但并不保证这种破坏不会发生。

以下是泛型特性带来的一些最为明显的变动。如果你想要知道更详细的介绍,那么可以参考泛型特性的提案或者语言规范

  • 函数类型声明的语法现在接受类型型参.
  • 在参数化的函数和类型后面,可以通过用方括号包裹一系列类型参数来进行实例化
  • 新标记 ~ 已经被添加到操作符和标点符号集合中
  • 接口类型的语法现在允许嵌入在任何类型里面(而不仅仅是类型名字),包括联合类型(union)和 ~T 类型。接口现在不仅定义方法也定义类型。
  • 新的预定义标志符 any 是空接口的别名(alias),它可以取代 interface{}
  • 新的预定义标志符 comparable 是一个接口,用于表示所有可以使用 == 或者 != 进行比较的类型。这一接口只能用作类型约束,又或者嵌入在类型约束当中。

现在有三个包在试验性地使用泛型,这些包都被放置在 x/exp 库当中。这些包的 API 不在 Go 1 的保证范围之内,随着我们对泛型的经验积累,它们可能会发生变化:

当前的泛型实现具有以下限制:

  • Go 编译器无法处理泛型函数或方法之内的类型声明,我们期望在 Go 1.19 提供这一特性。
  • Go 编译器不支持为预定义函数 realimagcomplex 的类型形参类型(type parameter type)设置实参(argument),我们期望在 Go 1.19 移除这一限制。
  • Go 编译器只有在类型形参类型 P 使用约束接口显式地声明了方法 m 的情况下,才能调用类型为 P 的值 x 的方法 m 。 与此类似,即使方法 m 已经事实上存在于所有实现了接口 P 的类型中,方法 x.m 和方法表达式 P.m 也只能在 P 显式地声明了它们的情况下使用。 我们期望在 Go 1.19 移除这一限制。
  • Go 编译器不支持访问结构字段 x.f , 当 x 为类型形参类型时,即使类型形参类型集合的所有类型都拥有字段 f ,Go 编译器也不支持访问结构字段 x.f 。我们期望在 Go 1.19 移除这一限制。
  • 不能将类型形参或者指向类型形参的指针嵌入到结构类型中用作匿名字段。 类似地,在接口类型中嵌入类型形参也是不允许的。 目前尙不清楚何时会允许这些操作。
  • 拥有多种意义(term)的联合元素不能包含带有非空方法集合的接口类型。 目前尚不清楚何时会允许这一操作。

泛型还将对 Go 的生态系统产生巨大影响。 尽管我们已经更新了几个核心工具以便让其支持泛型,但还有很多工作有待完成。 我们还需要一些时间才能将语言的改变反映到余下的工具、文档和函数库里面。

觉得所有编程语言最终都难逃变成 C++ 命运的黄健宏
2022.3.19