Is и As
Кто-то сравнил ошибки по тексту, и после добавления контекста проверки замолчали. Клиент запрашивал несуществующий файл и получал 500 вместо 404. Миша перевёл различение ошибок на поиск по всей цепочке.
errors.Is ищет ошибку по значению (сентинел), errors.As — по типу, и обе проходят по всей цепочке оборачивания.
Is — по значению
if errors.Is(err, ErrNotFound) {
// где-то в цепочке есть ErrNotFound
}
Is разворачивает цепочку и сравнивает каждый слой с целевой ошибкой. Для обычных сентинелов это ==, но типы ошибок могут определять свой Is — например, сравнивать по коду ошибки, а не по указателю.
As — по типу
var pathErr *fs.PathError
if errors.As(err, &pathErr) {
fmt.Println(pathErr.Path) // доступ к полям конкретного типа
}
As ищет в цепочке ошибку нужного типа и, если нашёл, кладёт её в pathErr. Заметь, что передаётся указатель на указатель: As присваивает через него. As паникует, если целевая переменная — не указатель на ошибку или интерфейс.
Is против ==
Почему не err == ErrNotFound? Потому что == смотрит только верхний слой — а там обычно обёртка с контекстом. Is пробивает всю цепочку:
wrapped := fmt.Errorf("контекст: %w", ErrNotFound)
fmt.Println(wrapped == ErrNotFound) // false
fmt.Println(errors.Is(wrapped, ErrNotFound)) // true
Вы узнаете:
- как написать пользовательские
IsиAsдля ошибок с полями - как
errors.Joinвзаимодействует сIsиAs
Проверьте себя
wrapped := fmt.Errorf("запрос к БД: %w", ErrNotFound). Чему равны wrapped == ErrNotFound и errors.Is(wrapped, ErrNotFound)?
var s string; errors.As(err, s). Что произойдёт?
Чем errors.Is отличается от errors.As?
правильных ответов
Is — «есть ли в цепочке эта ошибка по значению»; As — «есть ли в цепочке ошибка такого типа». Обе идут сквозь %w-обёртки.
10 000 ₽ · 60 минут · онлайн