


В Go ошибка может получить дополнительный контекст через fmt.Errorf с %w. После этого прямое сравнение или утверждение типа внешней ошибки часто уже не подходит. errors.Is проверяет совпадение с целевой ошибкой в цепочке, а errors.As находит ошибку подходящего типа и позволяет прочитать её поля.
В разборе Mentoring.digital используем учебную ошибку проверки поля. Пример работает на Go 1.13 и новее и не обращается к внешним сервисам.
package main
import (
"errors"
"fmt"
)
var ErrRequired = errors.New("required value")
type FieldError struct {
Field string
Err error
}
func (e *FieldError) Error() string {
return fmt.Sprintf("field %s: %v", e.Field, e.Err)
}
func (e *FieldError) Unwrap() error {
return e.Err
}
func validateName(name string) error {
if name == "" {
return &FieldError{Field: "name", Err: ErrRequired}
}
return nil
}
func main() {
cause := validateName("")
if cause == nil {
return
}
err := fmt.Errorf("create profile: %w", cause)
fmt.Println(errors.Is(err, ErrRequired))
var fieldErr *FieldError
if errors.As(err, &fieldErr) {
fmt.Println(fieldErr.Field)
}
}
Сохраните код в main.go и запустите go run main.go. Ожидаются строки true и name. Первая проверка находит ErrRequired за двумя обёртками. Вторая находит FieldError и предоставляет имя поля. В нашей функции пустота означает только name == "": строка из пробелов этим правилом не запрещена.
ErrRequired создаётся один раз как отдельное значение. Новый вызов errors.New с тем же сообщением создаст другую ошибку. Поэтому не пересоздавайте целевую ошибку прямо внутри errors.Is и не сравнивайте строки Error() ради бизнес-решения.
Сообщение можно дополнить названием операции, а пользовательский текст можно перевести. При сохранённой цепочке проверка причины продолжит работать. Не добавляйте в сообщения пароль, токен или содержимое личных данных только ради удобства диагностики.
Переменная fieldErr имеет тип *FieldError. В errors.As передаётся адрес переменной: &fieldErr. Функция при успехе записывает туда найденный указатель. Это не создание нового FieldError и не преобразование строки в структуру.
Обычное err.(*FieldError) проверяет только внешний объект. В примере внешним объектом является результат fmt.Errorf, поэтому прямое утверждение типа не найдёт внутреннюю структуру. As проходит по раскрываемым ошибкам, учитывая правила сопоставления типов.
Оборачивание через %w делает внутреннюю причину доступной вызывающему коду. Это часть контракта API: подумайте, готовы ли вы сохранять такую причину при смене реализации. Не превращайте каждую ожидаемую ошибку ввода в panic.
Практику обработки ошибок можно включить в курс Go с наставником. Для ревью реального контракта покажите ментору по Go и место возникновения ошибки, и код, который принимает по ней решение.