Since Go 1.18 supported generics, the meaning of the Go interface has changed radically, and is used as a type constraint for generics in addition to the set of methods it previously represented. interface is no longer the simple interface it once was.

In Go 1.17.x and previous versions, interface was defined as follows

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.

In Go 1.18, the interface definition was changed to

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.

So in a nutshell, previously interfaces defined collections of methods, now interfaces define collections of types. The use of interfaces has been extended.

The definition of interface has also been extended. Previously, an interface definition could only contain a method element:

1
2
3
4
5
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

Interface definitions can now contain type elements in addition to method elements:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
interface {
	int
}
// An interface representing all types with underlying type int.
interface {
	~int
}
// An interface representing all types with underlying type int which implement the String method.
interface {
	~int
	String() string
}
// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
	int
	string
}

The type element contains the type ( T ) or approximate type ( ~T ) or union element ( A|B|C|~D ).

However, because the definition and meaning of the interface has changed, there are some differences in the use of the interface. This article introduces them one by one with examples.

First of all, remember that Go 1.17.x and previous versions of interfaces are used in the same way in Go 1.18, and the way they are used remains the same. What has changed is that the interface has some restrictions when it comes to type elements or doing type constraints.

The type of the approximation element T must be the underlying type itself, and cannot be an interface type

1
2
3
4
5
6
// 错误的定义!
type MyInt int
type I0 interface {
	~MyInt // 错误! MyInt不是underlying type, int才是
	~error // 错误! error是接口
}

A union type element cannot be a type parameter

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

The non-interface elements of a union element must be disjoint

Two disjoint means that the intersection of two is the empty set, for example, the intersection of int|string is the empty set and the intersection of int|~int is int.

The non-interface elements in a union type must be non-intersecting.

The following definition is fine, because any is equivalent to interface{} :

1
2
func I3[K any, V interface{ int | any }]() {
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 错误! 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 }]() {
}

A union type element that contains more than one element cannot contain an interface type that contains non-empty methods, and cannot be comparable or embedded in a comparable.

This rule defines some restrictions on the use of interfaces as type elements.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 编译没问题,只包含一个元素
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]() {
}

elements containing non-interface types, proximate elements and union types that can only be used as type parameters, or other elements used as binding interfaces

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var (
    // 以下编译没问题 
	_ interface{}
	_ interface{ m() }
	_ interface{ io.Reader }
	_ interface {
		io.Reader
		io.Writer
	}
    // 以下不能编译,接口不能用作变量实例类型
	_ interface{ int }
	_ interface{ ~int }
	_ interface{ MyInt }
	A interface {
   	  int
	  m()
	}
    // 可以编译
	_ struct{ i int }
    // 下面一行不能编译,因为~int不能作为字段的类型
	_ struct{ i ~int }
    // 下面一行不能编译,因为constraints.Ordered只能用作类型约束
	_ struct{ i constraints.Ordered }
    // 下面两行能够编译,因为它们是接口类型,并且类型元素也是普通接口
    _ interface{ any }
	_ interface {
		interface {
			any
			m()
		}
	}
    // 不能编译,因为接口部署普通接口,而是类型约束
	_ interface {
		interface {
			int|~int
			m()
		}
	}
)

Interface type indefinite recursive embedding

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 错误! 不能自己嵌入自己
type Node interface {
	Node
}
// 错误! Tree不能通过TreeNode嵌入自己
type Tree interface {
	TreeNode
}
type TreeNode interface {
	Tree
}