Что вернёт errors.Is, если искомая ошибка равна nil, а проверяемая ошибка ненулевая и имеет собственный метод Is?
errors.Is вернёт false. Если искомая ошибка равна nil, функция сначала проверяет, равна ли nil сама проверяемая ошибка, и не вызывает пользовательский метод Is ненулевой ошибки.
В Go 1.13 появились стандартные механизмы проверки и обёртывания ошибок — errors.Is, errors.As и поддержка %w. Они решают проблему анализа цепочек ошибок без сравнения текстовых сообщений и без привязки вызывающего к конкретной реализации обёртки.
При этом у errors.Is есть специальные базовые случаи, которые обрабатываются до обхода цепочки и вызова пользовательских методов. Проверка nil — один из таких случаев.
Разработчик может ожидать, что errors.Is всегда будет проходить по цепочке и вызывать Is у текущей ошибки. Это неверно для nil-цели: пользовательский метод не может изменить смысл проверки и объявить ненулевую ошибку совпадающей с nil.
Если перепутать errors.Is(err, nil) с проверкой категории ошибки, можно получить ошибочную логику обработки. Для определения специального класса ошибок нужна ненулевая sentinel-ошибка или типизированная цель, а не nil.
Алгоритм errors.Is концептуально начинается с особой проверки цели. Если цель равна nil, результат определяется простым сравнением проверяемой ошибки с nil:
errors.Is(nil, nil) возвращает true;errors.Is(ненулеваяОшибка, nil) возвращает false;Is ненулевой ошибки в этих случаях не вызывается.Только при ненулевой цели начинается обычная обработка: сначала проверяется прямое совпадение, затем может быть вызван метод Is, после чего исследуются обёрнутые или объединённые причины.
Минимальная иллюстрация:
Метод Is предназначен для сопоставления с конкретной ненулевой категорией ошибки. Он не является механизмом переопределения нулевости интерфейса error и не должен использоваться для имитации nil.
Важно также отличать nil-интерфейс от интерфейса, содержащего типизированный nil-указатель. Последний сам по себе может быть ненулевым значением error, поэтому errors.Is(err, nil) способен вернуть false. Это связано с семантикой интерфейсов Go, а не с обходом цепочки ошибок.
В HTTP-сервисе общий обработчик ошибок пытается определить отсутствие ошибки через errors.Is(err, nil). Позже в коде появляется пользовательский тип ошибки с методом Is, который сопоставляет её с несколькими категориями. Разработчик ожидает, что этот метод будет вызван и сможет повлиять на результат проверки с nil.
Рассматривались два варианта. Первый — разрешить пользовательским ошибкам самостоятельно трактовать nil; это нарушает предсказуемую семантику отсутствия ошибки и усложняет анализ кода. Второй — использовать errors.Is(err, nil) только как эквивалент проверки отсутствия ошибки, а категории проверять через ненулевые sentinel-ошибки или типы.
Выбран второй вариант. В результате nil однозначно означает отсутствие ошибки, а пользовательские методы Is отвечают только за сопоставление с содержательными, ненулевыми целями. Это делает общий обработчик независимым от конкретных типов ошибок.
Вопрос: Вызывается ли метод Is проверяемой ошибки, если цель равна nil, но сама ошибка завернута в несколько слоёв?
Ответ: Нет. Специальная проверка цели выполняется до обхода цепочки, поэтому ни внешняя обёртка, ни внутренние причины, ни их методы Is не анализируются. Для ненулевой цели обход, напротив, может продолжиться через Unwrap.
Вопрос: Может ли errors.Is вернуть true для ненулевой ошибки и nil-цели из-за того, что одна из внутренних причин равна nil?
Ответ: Нет. Наличие nil внутри цепочки не превращает всю цепочку в отсутствие ошибки. Обёртка остаётся ненулевой ошибкой, а errors.Is(обёртка, nil) возвращает false; nil-цель проверяет сам переданный корень, а не наличие пустой причины внутри него.
Вопрос: Почему проверка err == nil обычно предпочтительнее для обычного определения отсутствия ошибки, хотя errors.Is(err, nil) даёт тот же логический результат?
Ответ: err == nil прямо выражает намерение и не требует обхода цепочки или знания API errors. errors.Is(err, nil) корректен, но избыточен: его смысл особенно полезен для проверки ненулевых причин, категорий и типов. В прикладном коде err == nil обычно лучше читается как базовая проверка отсутствия ошибки.