Обычное
err == myErr в Go работает не всегда так, как ты ожидаешь. Особенно если ты оборачиваешь ошибку через fmt.Errorf("context: %w", err). В таких случаях Go предоставляет более мощные инструменты — errors.Is и errors.As.❓Как работает
errors.IsКогда ты используешь
%w в fmt.Errorf (врапинг ошибки), ты как бы упаковываешь одну ошибку внутрь другой, добавляя контекст. errors.Is умеет разворачивать такие обёртки и проверять, есть ли внутри нужная ошибка:var ErrNotFound = errors.New("not found")
err := fmt.Errorf("ошибка в поиске: %w", ErrNotFound)
if errors.Is(err, ErrNotFound) {
fmt.Println("Ничего не найдено")
}Под капотом
errors.Is делает так: если ошибка обёрнута через %w, она вызывает Unwrap() и идёт по цепочке, пока не найдёт нужную. Если ты просто сравнишь err == ErrNotFound, то получишь false, потому что это уже не тот же объект — он обёрнут.❓А что делает
errors.AsИногда нужно не просто узнать, была ли ошибка определённого типа, но и получить доступ к её полям. Например:
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintf("code: %d, msg: %s", e.Code, e.Msg)
}
err := &MyError{Code: 499, Msg: "custom"}
var myErr *MyError
if errors.As(err, &myErr) {
fmt.Println("У нас ошибка кода", myErr.Code)
}Ты можешь доставать полезные данные из ошибки, если она реализует нужный тип. Это особенно удобно, когда у тебя есть логика, завязанная на тип ошибки (например, логирование по коду, трассировка и т.д.).
❓Как кастомизировать сравнение — метод
Is()Ты даже можешь сам определить, когда твоя ошибка считается «равной» другой. Для этого нужно реализовать метод
Is(target error) в своей реализации:type HttpError struct {
Code int
Msg string
}
func (e *HttpError) Error() string {
return fmt.Sprintf("HTTP %d: %s", e.Code, e.Msg)
}
func (e *HttpError) Is(target error) bool {
t, ok := target.(*HttpError)
return ok && e.Code == t.Code
}Теперь, даже если сообщения об ошибках разные, но коды совпадают —
errors.Is вернёт true:var Err404 = &HttpError{Code: 404}
err := fmt.Errorf("обёрнутая ошибка: %w", &HttpError{Code: 404, Msg: "page not found"})
if errors.Is(err, Err404) {
fmt.Println("Обнаружена 404")
}❓Когда и где это реально нужно (примеры из практики)
— В библиотеках: ты возвращаешь ошибки вроде
ErrInvalidConfig или ErrNotConnected, и хочешь, чтобы пользователи могли их отловить независимо от контекста— В сервисах: API может возвращать ошибки с кодами, а внутри тебе удобно описывать их кастомными типами — и потом ловить их по
errors.Is— В middleware: например, хочешь логировать всё, кроме
ErrUnauthorized — тогда errors.Is(err, ErrUnauthorized) спасёт от лишнего шума— В gRPC/HTTP-хендлерах: удобно классифицировать ошибки и автоматически подставлять нужный статус код в ответе
— В CLI-инструментах: логика выхода с кодами ошибок может зависеть от типа ошибки, а не только от текста
Если ты до сих пор сравниваешь ошибки через
==, самое время освоить errors.Is и errors.As. Это делает твой код гибким, читаемым и гораздо ближе к надёжной архитектуре. Go даёт тебе все инструменты — просто используй их.Больше примеров работы с ошибками даю в бесплатном уроке на своем курсе Go!
P.S. все еще действует промокод на скидку 30% -
FEIN
