Nil-интерфейс — это особое состояние интерфейса в Go, которое часто вызывает путаницу у разработчиков. Давайте разберём это понятие детально.
В Go существует два принципиально разных случая, когда интерфейс можно считать "nil":
var i interface{}
fmt.Println(i == nil) // true
В этом случае:
_type
в eface
или itab
в iface
) равен nildata
) равен nilvar s *string // s == nil
var i interface{} = s
fmt.Println(i == nil) // false!
Здесь:
*string
)Такое поведение может приводить к неожиданным ошибкам:
func doSomething(v interface{}) {
if v != nil {
// Этот код выполнится, даже если v содержит nil-указатель!
fmt.Println("Not nil!", reflect.TypeOf(v))
}
}
var s *string
doSomething(s) // Выведет "Not nil! *string"
Для полной проверки можно использовать рефлексию:
func isNil(i interface{}) bool {
if i == nil {
return true
}
val := reflect.ValueOf(i)
return val.Kind() == reflect.Ptr && val.IsNil()
}
Для интерфейсов с методами (iface
):
type iface struct {
tab *itab // nil для истинного nil-интерфейса
data unsafe.Pointer // nil в обоих случаях
}
Для пустых интерфейсов (eface
):
type eface struct {
_type *_type // nil для истинного nil-интерфейса
data unsafe.Pointer // nil в обоих случаях
}
err == nil
, но возвращается error
интерфейс с nil-значением:func returnsError() error {
var p *MyError // nil
return p // != nil!
}
iface
/eface
)