💬 Почему значение ошибки типа nil не равно nil?



Под капотом интерфейсы реализованы как два элемента: тип T и значение V. V — это конкретное значение, такое как int, структура или указатель. Например, если мы сохраняем значение int 3 в интерфейсе, полученное значение интерфейса схематически будет (T=int, V=3).



Значение V также известно как динамическое значение интерфейса, поскольку одна и та же переменная интерфейса может хранить разные значения V (и соответствующие типы T) во время выполнения программы.



Значение интерфейса является nil только если и V, и T не установлены. В частности, nil интерфейс всегда будет содержать nil тип. Если мы сохраняем nil указатель типа *int внутри значения интерфейса, внутренний тип будет *int независимо от значения указателя: (T=*int, V=nil). Таким образом, значение интерфейса будет не nil, даже когда значение указателя V внутри является nil.



Эта ситуация может быть запутанной и возникает, когда nil значение сохраняется внутри значения интерфейса, например, в возвращаемом значении ошибки:



func returnsError() error {

var p *MyError = nil

if bad() {

p = ErrBad

}

return p // Всегда вернет не nil ошибку.

}





Если всё идет хорошо, функция возвращает nil p, так что возвращаемое значение — это значение интерфейса ошибки, содержащее (T=*MyError, V=nil). Это означает, что если вызывающий сравнивает возвращенную ошибку с nil, он всегда будет видеть, как будто произошла ошибка, даже если ничего плохого не случилось. Чтобы вернуть правильную nil ошибку вызывающему, функция должна явно вернуть nil:



func returnsError() error {

if bad() {

return ErrBad

}

return nil

}





Хорошей практикой для функций, возвращающих ошибки, является использование типа error в их сигнатуре (как сделано выше), а не конкретного типа, например *MyError, чтобы помочь гарантировать, что ошибка создается правильно.