Ответ
Значение интерфейсного типа в Go равно nil
только тогда, когда и его динамический тип, и динамическое значение равны nil
.
Интерфейс в Go внутренне представляет собой кортеж из двух указателей: (тип, значение).
Ключевая концепция
var i interface{}
: Здесьi
равенnil
, потому что и тип, и значение не установлены.var p *int
: Указательp
равенnil
.i = p
: Теперь интерфейсi
не равенnil
. Его тип становится*int
, а значение —nil
.
Это частая «ловушка» в Go, когда функция, возвращающая интерфейс, может вернуть не nil
-интерфейс, который содержит nil
-указатель.
Пример
package main
import (
"fmt"
"reflect"
)
func main() {
var i interface{} // тип=nil, значение=nil
fmt.Printf("1. i: (value: %v, type: %T), is nil? %vn", i, i, i == nil)
var p *int // p - это nil-указатель
i = p // тип=*int, значение=nil
fmt.Printf("2. i: (value: %v, type: %T), is nil? %vn", i, i, i == nil)
// Как правильно проверить, что значение внутри интерфейса - nil?
if i != nil && reflect.ValueOf(i).IsNil() {
fmt.Println("3. Интерфейс не nil, но его внутреннее значение - nil.")
}
}
// Вывод:
// 1. i: (value: <nil>, type: <nil>), is nil? true
// 2. i: (value: <nil>, type: *int), is nil? false
// 3. Интерфейс не nil, но его внутреннее значение - nil.
Как правильно проверять
- Простая проверка
if i == nil
: Сработает только если и тип, и значениеnil
. - Проверка через рефлексию
reflect.ValueOf(i).IsNil()
: Надежный способ проверить, является ли внутреннее значение интерфейсаnil
(например,nil
-указатель,nil
-слайс,nil
-канал и т.д.). Следует использовать с осторожностью, так как рефлексия медленнее.