Тип error: возврат, проверка и оборачивание
Содержание курса
Создание и оборачивание ошибок: errors.New, fmt.Errorf с %w и errors.Is
Самый простой способ создать ошибку — errors.New:
import "errors"
var ErrNotPositive = errors.New("число должно быть положительным")
errors.New возвращает значение типа error с фиксированным сообщением. Объявлять такие переменные на уровне пакета — стандартная практика: они называются сигнальными ошибками (sentinel errors) и позволяют сравнивать конкретные причины сбоя.
Но что если функция нашла ошибку глубже и хочет добавить контекст, не теряя оригинал? Здесь нужен fmt.Errorf с глаголом %w:
import "fmt"
func process(n int) error {
err := validate(n)
if err != nil {
return fmt.Errorf("process: %w", err)
}
return nil
}
%w именно оборачивает ошибку: новый объект содержит текст "process: число должно быть положительным", но внутри хранит ссылку на исходный ErrNotPositive. Это не то же самое, что fmt.Sprintf — у него нет %w, и цепочка была бы потеряна.
Теперь вопрос: как вызывающий код определит, что внутри обёртки именно ErrNotPositive, а не другая ошибка? Прямое сравнение err == ErrNotPositive не пройдёт — err уже другой объект.
err := process(-1)
fmt.Println(err == ErrNotPositive) // false — это обёртка
fmt.Println(errors.Is(err, ErrNotPositive)) // true — разворачивает цепочку
errors.Is(err, target) проходит по всей цепочке обёрток и возвращает true, как только находит target. Цепочка может быть сколь угодно длинной — errors.Is доберётся до корня.
Практический вывод: errors.New создаёт «якорь», fmt.Errorf %w добавляет контекст на каждом уровне, errors.Is находит якорь в любой глубине обёртки.
