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:
Interface definitions can now contain type elements in addition to method elements:
|
|
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
A union type element cannot be a type parameter
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{}
:
|
|
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.
|
|
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
|
|