Для чего используется пакет errors в Go
Что проверяют
Знаете ли вы, как в Go устроены ошибки после появления обёртывания, а не только errors.New и if err != nil. Следом обычно спрашивают, чем errors.Is отличается от errors.As и почему сравнение через == ломается.
Как отвечать
Ошибка в Go это любое значение, реализующее интерфейс error с методом Error() string. Пакет errors даёт инструменты для создания ошибок и разбора цепочки обёрток:
errors.New: создаёт простую ошибку с текстом. На уровне пакета так объявляют sentinel-ошибки вродеio.EOFилиsql.ErrNoRows, с которыми потом сравнивают;- обёртывание:
fmt.Errorf("load user %d: %w", id, err)добавляет контекст и сохраняет исходную ошибку внутри. Сама функция живёт в пакетеfmt, аerrorsумеет эту цепочку разбирать; errors.Is: проходит по цепочке и ищет конкретное значение, напримерerrors.Is(err, sql.ErrNoRows);errors.As: ищет в цепочке ошибку нужного типа и записывает её в переменную:var pe *fs.PathError; if errors.As(err, &pe) {...}. Нужен, когда из ошибки надо достать поля;errors.Unwrap: снимает один слой обёртки, в прикладном коде нужен редко;errors.Join: объединяет несколько ошибок в одну (с Go 1.20),IsиAsнаходят любую из них. С той же версииfmt.Errorfпринимает несколько%w.
Хорошо добавить про свои типы ошибок: структура с полями и методом Error(), при необходимости с Unwrap(). И про стиль: контекст добавляют там, где он появился, а логируют ошибку один раз, на верхнем уровне.
Где спотыкаются
Сравнивают через err == sql.ErrNoRows после того, как ниже по стеку ошибку обернули. Сравнение молча перестаёт срабатывать, нужен errors.Is.
Передают в errors.As значение вместо указателя на переменную. Такой вызов паникует.
Возвращают из функции типизированный nil, например var e *MyErr; return e. Интерфейс error при этом не равен nil, и проверка err != nil срабатывает на пустом месте.