РЕПЕТИТОР математика физика информатика
Для школьников и студентов. Подтягивание пробелов. ЦЭ, ЦТ, ОГЭ, ЕГЭ.
Идет набор на ЛЕТО. Жмите для подробностей:)
477 of 1593 menu

Интерфейс и nil-указатель

В Go интерфейс хранит пару значений: динамический тип и динамическое значение. Интерфейс считается nil только тогда, когда оба этих поля равны nil. Если в интерфейс сохранить nil-указатель на конкретный тип, то динамический тип становится известным, и интерфейс перестает быть nil, даже если его значение равно nil.

Это одна из самых частых ловушек в Go, которая приводит к неожиданному поведению при проверке if err == nil, если error — это интерфейс, а возвращаемая ошибка реализована через указатель на структуру.

Синтаксис

Объявление переменной типа интерфейс:

var i interface{} // nil-интерфейс var p *int // nil-указатель i = p // i больше не nil

Пример

Рассмотрим базовый пример, демонстрирующий разницу между nil-интерфейсом и интерфейсом, содержащим nil-указатель:

package main import "fmt" func main() { var i interface{} // nil-интерфейс fmt.Println(i == nil) var p *int // nil-указатель i = p fmt.Println(i == nil) }

Результат выполнения кода:

true false

Пример

Теперь рассмотрим более жизненный пример с интерфейсом error. Создадим свою структуру ошибки и посмотрим, как поведет себя проверка на nil:

package main import "fmt" type MyError struct { msg string } func (e *MyError) Error() string { return e.msg } func getError() error { var e *MyError = nil return e } func main() { err := getError() if err == nil { fmt.Println("err is nil") } else { fmt.Println("err is not nil") } }

Результат выполнения кода:

"err is not nil"

Несмотря на то, что мы вернули nil-указатель, функция getError возвращает интерфейс error, в котором сохранен тип *MyError и значение nil. Поэтому интерфейс не равен nil.

Пример

Правильный способ обработки таких ситуаций — возвращать nil только в том случае, если ошибки действительно нет:

package main import "fmt" type MyError struct { msg string } func (e *MyError) Error() string { return e.msg } func getError(flag bool) error { if flag { return &MyError{msg: "something went wrong"} } return nil // возвращаем именно nil, а не nil-указатель } func main() { err := getError(false) if err == nil { fmt.Println("err is nil") } else { fmt.Println("err is not nil") } }

Результат выполнения кода:

"err is nil"

Смотрите также

  • понятие nil-интерфейса,
    который не содержит ни типа, ни значения
  • механизм type assertion,
    позволяющий извлечь динамическое значение из интерфейса
  • конструкцию type switch,
    которая выполняет действия в зависимости от динамического типа
  • интерфейс error,
    который является стандартным интерфейсом для представления ошибок
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить