Go-фича: Безопасная проверка ошибок
Часть серии Принято! В ней простыми словами объясняются новые фичи Go.
Проверка типа ошибки через errors.AsType — современная и безопасная альтернатива errors.As.
Версия 1.26 • Станд. библиотека • Важно
Что
Новая функция errors.AsType — это generic-версия errors.As:
// go 1.13+
func As(err error, target any) bool
// go 1.26+
func AsType[E error](err error) (E, bool)
Она безопасная (type-safe), работает быстрее и проще в использовании:
// используем errors.As
var appErr AppError
if errors.As(err, &appErr) {
fmt.Println("Got an AppError:", appErr)
}
// используем errors.AsType
if appErr, ok := errors.AsType[AppError](err); ok {
fmt.Println("Got an AppError:", appErr)
}
errors.As не объявлена устаревшей (пока), но для нового кода рекомендуется использовать errors.AsType.
Зачем
Функция errors.As требует заранее объявить переменную ошибки нужного типа и передать указатель на нее:
var appErr AppError
if errors.As(err, &appErr) {
fmt.Println("Got an AppError:", appErr)
}
Код получается довольно громоздким, особенно если нужно проверить несколько типов ошибок:
var connErr *net.OpError
var dnsErr *net.DNSError
if errors.As(err, &connErr) {
fmt.Println("Network operation failed:", connErr.Op)
} else if errors.As(err, &dnsErr) {
fmt.Println("DNS resolution failed:", dnsErr.Name)
} else {
fmt.Println("Unknown error")
}
С помощью generic-функции errors.AsType можно указать тип ошибки прямо при вызове. Это делает код короче и позволяет держать переменные ошибок внутри блоков if:
if connErr, ok := errors.AsType[*net.OpError](err); ok {
fmt.Println("Network operation failed:", connErr.Op)
} else if dnsErr, ok := errors.AsType[*net.DNSError](err); ok {
fmt.Println("DNS resolution failed:", dnsErr.Name)
} else {
fmt.Println("Unknown error")
}
Кроме того, As использует рефлексию и может вызвать панику, если использовать ее неправильно (например, если передать не указатель или тип, который не реализует error). Обычно статические анализаторы находят такие ошибки, но у AsType есть несколько преимуществ:
- Не использует рефлексию.
- Нет паники во время выполнения.
- Меньше аллокаций памяти.
- Проверка типов на этапе компиляции.
- Работает быстрее.
Наконец, AsType умеет все то же, что и As, так что ее можно спокойно использовать вместо As в новом коде.
Подробности
Добавить в пакет errors функцию AsType:
// AsType finds the first error in err's tree that matches the type E,
// and if one is found, returns that error value and true. Otherwise, it
// returns the zero value of E and false.
//
// The tree consists of err itself, followed by the errors obtained by
// repeatedly calling its Unwrap() error or Unwrap() []error method.
// When err wraps multiple errors, AsType examines err followed by a
// depth-first traversal of its children.
//
// An error err matches the type E if the type assertion err.(E) holds,
// or if the error has a method As(any) bool such that err.As(target)
// returns true when target is a non-nil *E. In the latter case, the As
// method is responsible for setting target.
func AsType[E error](err error) (E, bool)
Рекомендовать использовать AsType вместо As:
// As finds the first error in err's tree that matches target, and if one
// is found, sets target to that error value and returns true. Otherwise,
// it returns false.
// ...
// For most uses, prefer [AsType]. As is equivalent to [AsType] but sets its
// target argument rather than returning the matching error and doesn't require
// its target argument to implement error.
// ...
func As(err error, target any) bool
Пример
Открыть файл и проверить, связана ли ошибка с путем к файлу:
// go 1.25
var pathError *fs.PathError
if _, err := os.Open("non-existing"); err != nil {
if errors.As(err, &pathError) {
fmt.Println("Failed at path:", pathError.Path)
} else {
fmt.Println(err)
}
}
Failed at path: non-existing
// go 1.26
if _, err := os.Open("non-existing"); err != nil {
if pathError, ok := errors.AsType[*fs.PathError](err); ok {
fmt.Println("Failed at path:", pathError.Path)
} else {
fmt.Println(err)
}
}
Failed at path: non-existing
Ссылки
★ Подписывайтесь на канал и проходите курсы.