An interface type defines a type set. A variable of interface type can store a
value of any type in that set, and such a type is said to implement the interface (spec:
Interface types). The zero value of an interface variable is nil.
InterfaceType = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem = MethodElem | TypeElem .
MethodElem = MethodName Signature .
MethodName = identifier .
TypeElem = TypeTerm { "|" TypeTerm } .
TypeTerm = Type | UnderlyingType .
UnderlyingType = "~" Type .
Basic interfaces
In its simplest form an interface lists methods. Its type set is the set of types that implement all of them, and its method set is exactly those methods. Interfaces whose type sets can be defined entirely by a list of methods are basic interfaces.
type File interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
}
- Explicitly listed method names must be unique and not blank.
- A type does not declare that it implements an interface. Having the methods is enough. Several types may implement one interface, and a type may implement many.
- The empty interface
interface{}is implemented by all types. The predeclaredany(Go 1.18) is an alias for it.
Embedded interfaces
An interface T may use an interface type name E as an element,
which embeds E in T. The type set of T is the
intersection of the type sets of its explicit methods and its embedded interfaces.
type Reader interface {
Read(p []byte) (n int, err error)
Close() error
}
type Writer interface {
Write(p []byte) (n int, err error)
Close() error
}
// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
Reader
Writer
}
When embedding, methods with the same name must have identical signatures, otherwise the declaration is an error. An interface may not embed a type element that is, contains or embeds itself, directly or indirectly.
General interfaces and type sets
Since Go 1.18 an interface element may also be a type term: a type T, a term
~T for an underlying type, or a union of terms. The type set is defined as
follows (spec: General interfaces):
| Element | Type set |
|---|---|
| Empty interface | All non-interface types |
| Non-empty interface | The intersection of the type sets of its elements |
| Method specification | All non-interface types whose method sets include that method |
Non-interface term T | Just that type |
~T | All types whose underlying type is T |
Union t1 | t2 | … | tn | The union of the type sets of its terms |
“All non-interface types” means all possible types in all possible programs, so an interface’s type set never contains an interface type.
Type elements
interface { int } // only the type int
interface { ~int } // all types with underlying type int
interface { ~int; String() string } // those with a String method too
interface { int; string } // empty: no type is both an int and a string
// The Float interface represents all floating-point types,
// including defined types whose underlying types are float32 or float64.
type Float interface {
~float32 | ~float64
}
- In
~T, the underlying type ofTmust beTitself andTcannot be an interface. - The type
TinTor~Tcannot be a type parameter. - The non-interface terms of a union must have pairwise disjoint type sets:
~int | MyIntis invalid whenMyInthas underlying typeint. - As an implementation restriction, a union with more than one term cannot contain
comparableor interfaces that specify methods, or that embedcomparableor interfaces that specify methods. - Interfaces that are not basic may only be used as type constraints or as elements of other constraints. They cannot be the types of values or variables, or components of other non-interface types.
var x Float // illegal: Float is not a basic interface
type Floatish struct {
f Float // illegal
}
Implementing an interface
A type T implements an interface I if T is not an
interface and is in the type set of I, or T is an interface and its
type set is a subset of that of I (spec: Implementing an interface). A value of
type T implements an interface if T does.
Which methods a value has depends on its method
set, including the rule that a pointer type *T also has the value-receiver
methods of T, but not the other way round. A common compile-time check is a
blank declaration:
var _ io.Reader = (*File)(nil)
*File does not implement io.ReaderDynamic type and value
An interface variable has a static type, the interface, and at run time a dynamic type and value, those of whatever was assigned (spec: Variables). A type assertion or a type switch gets the dynamic value back. A call of an interface method runs the method of the dynamic value.
Two interface values are equal if they have identical dynamic types and equal dynamic values, or are both nil. Comparing two values with identical dynamic types of a non-comparable type panics at run time.
Nil interface values
An interface value is nil only if it has no dynamic type. Calling or evaluating
a method on a nil interface panics. Storing a nil pointer in an interface gives a non-nil
interface value, because the dynamic type is then set. The specification’s example shows the
state of a variable x of type interface{}:
var x interface{} // x is nil and has static type interface{}
var v *T // v has value nil, static type *T
x = 42 // x has value 42 and dynamic type int
x = v // x has value (*T)(nil) and dynamic type *T
fmt.Println(x == nil) // false: x has a dynamic type
func find() error {
var e *MyError = nil
return e // returns a non-nil error, with dynamic type *MyError
}
A method call on a typed nil inside an interface is allowed if the method does not need to dereference the receiver.