Interface Values

The value of the interface type and the interface value are two different concepts.

Go+ is a statically typed programming language, types are a compile time concept, so a type is not a value. The value of the type descriptor provides information about each type, such as its name and methods. In an interface value, the type component is represented by the appropriate type descriptor.

In Go+, interfaces variables are always initialize well-defined value as other type variables. The zero value for an interface has both its type and value components set to nil, for example:

import ( "bytes" "io" "os" ) var w io.Writer println w == nil var w2 io.Writer = os.Stdout w2 = nil // same as w, both w and w2 interface variable has zero value println w2 == nil var r *bytes.Reader println r == nil var r2 io.Reader = r // r2 has non-zero interface value. Its dynamic type is *bytes.Reader and its dynamic value is nil. So the result of r2 == nil is false if r2 != nil { slice := []byte{} r2.Read(slice) // panic: runtime error: invalid memory address or nil pointer dereference }

The interface variable w has zero value. So both its dynamic type and its dynamic value are nil. In this case, the result of if w == nil is true. The interface variable r2 has non-zero value. Its dynamic type is *bytes.Reader and its dynamic value is nil. So the result of r2 == nil is false.

Interface values may be compared using == and !=. Two interface values are equal if both are nil, or if their dynamic types are identical and their dynamic values are equal according to the usual behavior of == for that type. Because interface values are comparable, they may be used as the keys of a map or as the operand of a switch statement.

However, if two interface values are compared and have the same dynamic type, but that type is not comparable (a slice, for instance), then the comparison fails with a panic. For example

var x = []any{1, 2, 3} var y = []any{1, 2, 3} println(x == y) // panic: comparing uncomparable type []int

In this respect, interface types are unusual. Other types are either safely comparable (like basic types and pointers) or not comparable at all (like slices, maps, and functions), but when comparing interface values or aggregate types that contain interface values, we must be aware of the potential for a panic. A similar risk exists when using interfaces as map keys or switch operands. Only compare interface values if you are certain that they contain dynamic values of comparable types.

When handling errors, or during debugging, it is often helpful to report the dynamic type of an interface value. For that, we use the fmt package’s %T verb:

import ( "bytes" "fmt" "io" "os" ) var w io.Writer fmt.printf("%T\n", w) // "" w = os.Stdout fmt.printf("%T\n", w) // "*os.File" w = new(bytes.Buffer) fmt.printf("%T\n", w) // "*bytes.Buffer"

Internally, fmt uses reflection to obtain the name of the int erface’s dynamic type.

Next example: The error Interface