Функция объявлена возвращающей error, но внутри возвращает nil-указатель конкретного типа ошибки. Чему будет равно сравнение результата с nil и почему?
Результат будет не равен nil. При преобразовании nil-указателя в интерфейс error интерфейс получает динамический тип, поэтому сам интерфейс становится ненулевым, даже если его внутреннее значение — nil.
В Go ошибки представлены интерфейсом error, что позволяет возвращать значения разных конкретных типов через единый контракт. Такая модель отделяет тип ошибки от её текстового представления, но требует учитывать общие правила работы интерфейсов и nil.
Типичная ошибка возникает, когда функция возвращает указатель на пользовательский тип ошибки. Если указатель равен nil, но его возвращают как error, вызывающий код получает ненулевой интерфейс и ошибочно считает, что произошла ошибка.
Это может привести к преждевременному завершению операции, неверной статистике ошибок и последующим вызовам методов у nil-указателя. Безопасность такого вызова зависит от реализации метода: метод может обработать nil-приёмник, а может вызвать панику.
Интерфейсное значение состоит из динамического типа и динамического значения. Интерфейс равен nil только тогда, когда отсутствуют оба компонента. У интерфейса error, содержащего nil-указатель типа MyError, динамический тип присутствует, поэтому сравнение с nil даёт false.
Вызов load преобразует значение типа *MyError в интерфейс error. Исправлять проблему следует на границе функции: если указатель равен nil, нужно вернуть настоящий nil-интерфейс, например через условную проверку до возврата.
Ещё надёжнее — возвращать конкретную ошибку как значение, а не указатель, если тип допускает это без лишних копирований и сохраняет нужную семантику. Нельзя полагаться на проверку внутреннего указателя вызывающим кодом: она требует знания конкретного типа и разрушает абстракцию интерфейса.
В репозитории функция разбора конфигурации возвращала *ConfigError. При отсутствии ошибок она возвращала nil-указатель этого типа. После изменения сигнатуры на error HTTP-обработчик начал отвечать ошибкой даже для корректных конфигураций.
Рассматривались два варианта. Можно было проверять конкретный тип и его указатель в каждом вызывающем месте, но это распространяло детали реализации и создавало риск пропустить проверку. Можно было исправить функцию так, чтобы при отсутствии ошибки она возвращала именно nil-интерфейс; этот вариант сохранял обычный контракт error.
Выбрали второй вариант. После этого обычная проверка err != nil снова стала корректной, а конкретный тип ошибки остался деталью реализации функции.
Да, если этот указатель был преобразован в интерфейс. Важно различать локальную переменную указательного типа, равную nil, и интерфейс error, содержащий эту переменную: после преобразования динамический тип уже делает интерфейс ненулевым.
Может. Метод с указательным получателем способен явно обрабатывать nil-приёмник и возвращать безопасное сообщение. Но это не исправляет сам факт, что err != nil будет истинным, поэтому такая практика не заменяет возврат настоящего nil-интерфейса.
%w?Нет. Оборачивание создаёт новый ненулевой интерфейс ошибки и не превращает исходный typed nil в настоящий nil. Более того, дальнейшая работа с цепочкой ошибок может привести к неожиданным результатам или к вызову методов у nil-указателя, поэтому источник проблемы нужно исправлять до оборачивания.