Ответ
Это классический вопрос-ловушка в Go, который проверяет понимание внутреннего устройства интерфейсов.
Внутренне интерфейс в Go можно представить как кортеж (пару) из двух полей:
(тип, значение)
-
Nil-интерфейс (nil interface)
Интерфейс равен
nil
только в том случае, если и тип, и значение в его внутренней паре равныnil
.var i interface{} // Внутреннее представление i: (тип=nil, значение=nil) fmt.Println(i == nil) // Выведет: true
-
Интерфейс, содержащий nil-указатель
Это интерфейс, который не равен
nil
, но его динамическое значение — это указатель, который равенnil
.var p *int = nil var i interface{} = p // Внутреннее представление i: (тип=*int, значение=nil)
Здесь поле
тип
не равноnil
(оно равно*int
), поэтому и сам интерфейсi
не равенnil
.
Практический пример и "ловушка"
Это часто приводит к ошибкам при возврате ошибок из функций.
// Определим свой тип ошибки
type MyError struct{}
func (e *MyError) Error() string { return "my error" }
// Эта функция может вернуть nil-указатель типа *MyError
func doSomething() *MyError {
// ... какая-то логика
return nil // Возвращаем nil-указатель, а не nil-интерфейс
}
func main() {
// Тип переменной err - это интерфейс error
var err error
err = doSomething() // doSomething() вернула (*MyError)(nil)
// Внутреннее представление err: (тип=*MyError, значение=nil)
if err != nil {
// Этот блок выполнится, что может быть неожиданно!
fmt.Printf("Ошибка! Тип: %T, Значение: %vn", err, err)
// Вывод: Ошибка! Тип: *main.MyError, Значение: <nil>
}
}
Вывод:
- Nil-интерфейс:
(type=nil, value=nil)
. Он== nil
. - Не-nil интерфейс:
(type=T, value=V)
. Он!= nil
, даже еслиV
являетсяnil
-указателем.