Интерфейсы и nil в Go

Внутри Go переменная типа interface представлена как пара (type, value), где value — конкретное значение, а type — тип этого значения (на самом деле все чуть сложнее, но совсем уж в дебри не будем погружаться).

Например:

// переменная интерфейсного типа
var ivar interface{}

ivar = "hello"
// ivar представлена парой (string, "hello")

ivar = 3.14
// ival представлена парой (float64, 3.14)

Когда мы вызываем метод на интерфейсной переменной, Go вызывает соответствующий метод value:

type greeter interface {
    greet()
}

type english struct {
    name string
}
func (e *english) greet() {
    fmt.Println("Hello", e.name)
}

var ivar greeter = &english{"world"}
// type == *english, value == &english{"world"}
ivar.greet()
// вызывает value.greet() и печатает "Hello world"

Пока все логично. Но с nil вас может ожидать неожиданный поворот.

Пока интерфейсной переменной не присвоено значение, у нее и type, и value равны nil, поэтому сама переменная считается равной nil:

var ivar any
// type == nil, value == nil
// поэтому ivar == nil
fmt.Println(ivar == nil)
// true

Но как только интерфейсной переменной присвоили значение, type перестает быть nil. Поэтому переменная больше не равна nil, даже если value равно nil:

var e *english
fmt.Println(e == nil)
// true

ivar = e
// type == *english, value == nil
// поскольку type != nil, то ivar != nil
fmt.Println(ivar == nil)
// false

Это часто ставит в тупик начинающих (да и не только) разработчиков на Go, так что имейте в виду.

★ Подписывайтесь на новые заметки.