In Go we’ve two concepts related to interfaces:

Interface — set of methods required to implement such interface. It’s defined using keyword interface Interface type — variable of interface type which can hold any value implementing particular interface

Let’s discuss these topics in next two sections.

Defining an interface

Declaration of the interface type specifies methods belonging to it. Method is defined by its name and signature — input and result parameters:

type I interface {

m1()

m2(int)

m3(int) int

m4() int

}

Besides methods it’s allowed to embedded other interfaces — either defined in the same package or imported — using qualified name. It adds all methods from embedded interface:

import "fmt" type I interface {

m1()

} type J interface {

m2()

I

fmt.Stringer

}

Method set of interface J consists of:

m1() (from embedded interface I)

m2()

String() string (from embedded interface Stringer)

Order doesn’t matter so it’s possible to interleave method specifications and embedded interface types.

Both exported (starting with uppercase letter) and non-exported (starting with lowercase letter) methods from embedded interface type are added.

If I embeds an interface J which in turn embeds other interface K, then all methods from K will be also added to I:

type I interface {

J

i()

} type J interface {

K

j()

} type K interface {

k()

}

Method set of I contains of i() , j() and k() (source code).

Circular embedding of interfaces is disallowed and will be detected while compilation (source code):

type I interface {

J

i()

} type J interface {

K

j()

} type K interface {

k()

I

}

Compiler will raise an error interface type loop involving I .

Interface methods must have unique names (source code):

type I interface {

J

i()

} type J interface {

j()

i(int)

}

Otherwise compile-time error will be thrown: duplicate method i .

Composition of interfaces can be found throughout the standard library. One such example is io.ReadWriter:



Reader

Writer

} type ReadWriter interface {

We know how to create new interfaces. Let’s study now values of interface types…

Interface type value

Variable of interface type I can hold any value implementing I (source code):

type I interface {

method1()

} type T struct{} func (T) method1() {} func main() {

var i I = T{}

fmt.Println(i)

}

Here we have variable i which is of interface type I.

Static type vs dynamic type

Variables have type known at compilation phase. It’s specified while declaration, never changes and is known as static type (or just type). Variables of interface type also have static type which is an interface itself. They additionally have dynamic type so the type of assigned value (source code):

type I interface {

M()

} type T1 struct {} func (T1) M() {} type T2 struct {} func (T2) M() {} func main() {

var i I = T1{}

i = T2{}

_ = i

}

Static type of variable i is I. It won’t change. Dynamic type on the other hand is … well dynamic. After first assignment, dynamic type of i is T1. It isn’t set in stone though so the second assignment changes dynamic type of i to T2. When value of interface type value is nil (which is zero value for interfaces) then dynamic type is not set.

How to get dynamic type of interface type value?

Package reflect can be used to achieve that (source code):

fmt.Println(reflect.TypeOf(i).PkgPath(), reflect.TypeOf(i).Name())

fmt.Println(reflect.TypeOf(i).String())

Also fmt package is able to do that with format verb %T :

fmt.Printf("%T

", i)

Under the hood it uses reflect package though but this method works even when i is nil.

nil interface value

This time we’ll start with an example (source code):

type I interface {

M()

} type T struct {} func (T) M() {} func main() {

var t *T

if t == nil {

fmt.Println("t is nil")

} else {

fmt.Println("t is not nil")

}

var i I = t

if i == nil {

fmt.Println("i is nil")

} else {

fmt.Println("i is not nil")

}

}

Output:

t is nil

i is not nil

It might be surprising at first. Value we’re assigning to i variable is nil but then i isn’t equal to nil. Interface type value consists of two components:

dynamic type

dynamic value

Dynamic type has been discussed earlier (“Static type vs dynamic type” section). Dynamic value is the actual value assigned. In discussed snippet after assignment var i I = t , dynamic value of i is nil but dynamic type is *T. Function call fmt.Printf("%T

", i) after this assignment would print *main.T . Interface type value is nil iff both dynamic value and dynamic type are nil. The effect is that even if interface type value holds a nil pointer then such interface value is not nil. Known mistake is to return not initialized, non-interface type value from function returning interface type (source code):

type I interface {} type T struct {} func F() I {

var t *T

if false { // not reachable but it actually sets value

t = &T{}

}

return t

} func main() {

fmt.Printf("F() = %v

", F())

fmt.Printf("F() is nil: %v

", F() == nil)

fmt.Printf("type of F(): %T", F())

}

It prints:

F() = <nil>

F() is nil: false

type of F(): *main.T

just because interface type value returned from function has dynamic type set (*main.T), it isn’t equal to nil.

Empty interface

Method set of interface doesn’t have to contain at least one member. It can be completely empty (source code):

type I interface {} type T struct {} func (T) M() {} func main() {

var i I = T{}

_ = i

}

Empty interface is automatically satisfied by any type — so value of any type can be assigned to such interface type variable. The behaviour of dynamic or static types apply to empty interfaces in the same way as to non-empty interfaces. Prominent use of empty interface exists in variadic function fmt.Println.