2

Go泛型编程: interface 不再是那个interface

 2 years ago
source link: https://colobu.com/2022/01/08/the-interface-is-not-that-interface-in-go-1-18/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Go泛型编程: interface 不再是那个interface

自 Go 1.18 支持泛型后, Go interface 的意义已经彻彻底底的改变了,除了先前代表的方法集的意义外,还被用作泛型的类型约束(type constraint)的功能, interface已经不再是以前那个单纯的少年了。

在Go 1.17.x以及以前的版本中,interface是这样定义的:

An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

接口类型定义了一个方法集合,称之为接口(interface)。接口类型的变量可以存储任意的实现这个方法集合的类型,这种类型是此interface的超集。这种类型被称为实现了接口。接口类型的变量如果未初始化则它的值为nil。

在Go 1.18中,interface定义改变了:

An interface type defines a type set. A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface. The value of an uninitialized variable of interface type is nil.

接口类型定义了一个类型集合。接口类型的变量可以存储这个接口类型集合的任意一种类型的实例值。这种类型被称之为实现了这个接口。接口类型的变量如果未初始化则它的值为nil。

所以一句话,先前接口定义了方法集合,现在接口定义了类型集合。接口的用途也进行了扩展。

interface的定义也扩展了。先前,接口定义只能包含方法元素(method element):

interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error

现在接口定义除了方法元素外,还可以包含类型元素:

interface {
// An interface representing all types with underlying type int.
interface {
// An interface representing all types with underlying type int which implement the String method.
interface {
String() string
// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
string

类型元素包含类型(T)或者近似类型(~T)或者联合(union)元素(A|B|C|~D)。

但是,因为接口的定义和含义改变了,所以接口在使用的时候也有一些些不同。本文通过实例一一介绍。

首先记住一点,Go 1.17.x 及以前的版本中接口的使用方法在Go 1.18中照样使用,使用方法不变。变得是接口有类型元素或者做类型约束时的一些限制。

近似元素的类型T必须是底层类型(underlying type)自己,而且不能是接口类型

// 错误的定义!
type MyInt int
type I0 interface {
~MyInt // 错误! MyInt不是underlying type, int才是
~error // 错误! error是接口

联合(union)类型元素不能是类型参数(type parameter)

// 错误, interface{ K }中K是类型参数
func I1[K any, V interface{ K }]() {
// 错误, interface{ nt | K }中K 是类型参数
func I2[K any, V interface{ int | K }]() {

联合(union)类型元素的非接口元素必须是两两不相交

两两不相交意思是两两的交集是空集,比如 int|string的交集是空集,而int|~int的交集是int

联合类型中的非接口元素必须是两两不相交的。

下面的定义没问题,因为any等价于interface{}:

func I3[K any, V interface{ int | any }]() {
// 错误! int和!int相交
func I4[K any, V interface{ int | ~int }]() {
// 下面的定义没有问题。因为int和MyInt是两个类型,不相交
type MyInt int
func I5[K any, V interface{ int | MyInt }]() {
// 错误! int和~MyInt相交,交集是int
func I6[K any, V interface{ int | ~MyInt }]() {
// 错误! int和MyInt2是相同类型,相交
type MyInt2 = int
func I7[K any, V interface{ int | MyInt2 }]() {

联合(union)类型元素如果包含多于一个元素,不能包含包含非空方法的接口类型,也不能是comparable或者嵌入comparable

这条规则定义了接口作为类型元素的一些限制.

// 编译没问题,只包含一个元素
func I9[K interface{ io.Reader }]() {
// 错误!不能编译。因为包含了两个元素,而且无论是`io.Reader`还是`io.Writer`都包含方法
func I10[K interface{ io.Reader | io.Writer }]() {
// 编译正常,因为这是正常的接口,没有联合元素
func I11[K interface {
io.Reader
io.Writer
}]() {
// 错误! 联合类型多于一个元素,并且io.Reader包含方法
func I12[K interface{ io.Reader | int }]() {
// 错误! 不能编译.因为联合元素大于一个,并且不能是comparable
func I13[K comparable | int]() {
// 错误! 不能编译.因为联合元素大于一个,并且元素不能嵌入comparable
func I14[K interface{ comparable } | int]() {

包含非接口类型元素、近似元素和联合类型只能用作类型参数,或者其它用作约束接口的元素

// 以下编译没问题
_ interface{}
_ interface{ m() }
_ interface{ io.Reader }
_ interface {
io.Reader
io.Writer
// 以下不能编译,接口不能用作变量实例类型
_ interface{ int }
_ interface{ ~int }
_ interface{ MyInt }
A interface {
// 可以编译
_ struct{ i int }
// 下面一行不能编译,因为~int不能作为字段的类型
_ struct{ i ~int }
// 下面一行不能编译,因为constraints.Ordered只能用作类型约束
_ struct{ i constraints.Ordered }
// 下面两行能够编译,因为它们是接口类型,并且类型元素也是普通接口
_ interface{ any }
_ interface {
interface {
// 不能编译,因为接口部署普通接口,而是类型约束
_ interface {
interface {
int|~int

接口类型不定递归嵌入

// 错误! 不能自己嵌入自己
type Node interface {
// 错误! Tree不能通过TreeNode嵌入自己
type Tree interface {
TreeNode
type TreeNode interface {

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK