Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Отличие errors.Is от errors.As в Go
Оба метода из пакета errors (появились в Go 1.13) предназначены для работы с цепочками ошибок (error wrapping), но решают разные задачи. Их ключевое отличие заключается в типе сравнения: errors.Is проверяет значения ошибок, а errors.As — их типы.
errors.Is: Проверка на равенство значений
func Is(err, target error) bool — используется для проверки, содержится ли в цепочке ошибок err конкретная ошибка-образец (target). Работает через вызов метода Is() у ошибок в цепочке или прямое сравнение через ==.
Когда использовать:
- Когда нужно определить, произошла ли конкретная, заранее известная ошибка (например,
io.EOF,sql.ErrNoRows). - Для проверки ошибок-синглтонов или ошибок с кастомной логикой сравнения.
Пример:
import (
"errors"
"fmt"
"os"
)
func main() {
_, err := os.Open("non-existent-file.txt")
// Проверяем, является ли ошибка в цепочке os.ErrNotExist
if errors.Is(err, os.ErrNotExist) {
fmt.Println("Файл не найден!") // Выполнится
}
var pathError *os.PathError
// Для сравнения с типом ошибки используем errors.As
if errors.As(err, &pathError) {
fmt.Printf("Ошибка пути: %v\n", pathError.Path)
}
}
Как работает:
- Сравнивает
errсtargetчерез==. - Если
errреализует методIs(error) bool, вызывает его. - Если ошибка обёрнута (
wrapped), рекурсивно проходит по цепочке.
errors.As: Извлечение по типу
func As(err error, target any) bool — ищет в цепочке ошибок первую ошибку, которую можно присвоить переменной target (которая должна быть указателем на тип, реализующий error). Если находит — присваивает и возвращает true.
Когда использовать:
- Когда нужно извлечь ошибку конкретного типа из цепочки, чтобы получить доступ к её полям и методам.
- Для обработки ошибок определённых типов (например,
*os.PathError,*json.SyntaxErrorили собственных типов).
Пример:
import (
"encoding/json"
"errors"
"fmt"
)
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("код %d: %s", e.Code, e.Message)
}
func main() {
err := &MyError{Code: 404, Message: "Ресурс не найден"}
wrappedErr := fmt.Errorf("операция завершилась неудачно: %w", err)
var myErr *MyError
// Пытаемся извлечь ошибку типа *MyError из цепочки
if errors.As(wrappedErr, &myErr) {
fmt.Printf("Поймана MyError: код=%d, сообщение=%s\n",
myErr.Code, myErr.Message) // Код=404, сообщение="Ресурс не найден"
}
// Сравнение с errors.Is не сработает, так как это разные экземпляры
fmt.Println(errors.Is(wrappedErr, err)) // true (благодаря методу Is, если бы он был)
fmt.Println(errors.Is(wrappedErr, &MyError{Code: 404, Message: "Ресурс не найден"})) // false
}
Как работает:
- Проверяет, можно ли присвоить
err(или ошибку из цепочки) в переменнуюtarget. - Если да — присваивает и возвращает
true. - Также рекурсивно проходит по цепочке обёрнутых ошибок.
Сравнительная таблица
| Критерий | errors.Is | errors.As |
|---|---|---|
| Основная цель | Проверить, есть ли ошибка-образец в цепочке | Извлечь ошибку конкретного типа из цепочки |
| Механизм | Сравнение значений (через == или метод Is()) | Проверка присваиваемости типов |
| Возвращаемое значение | bool (найдена или нет) | bool + присваивание в target |
Аргумент target | error (обычно синглтон) | Указатель на тип ошибки (например, *MyError) |
| Типичный use-case | if errors.Is(err, io.EOF) | if errors.As(err, &pathError) |
Важные нюансы
- Порядок аргументов: Оба метода принимают
errпервым аргументом, ноtargetразный. - Цепочка ошибок: Оба метода рекурсивно разворачивают цепочку, созданную через
fmt.Errorfс%wили кастомные обёртки. - Производительность:
errors.Isобычно легче, так как сравнивает значения, аerrors.Asиспользует рефлексию (reflect) для проверки типов. - Совместное использование: Часто применяются вместе:
if errors.Is(err, sql.ErrNoRows) { // Обработка отсутствия данных } else if var dbErr *mysql.MySQLError; errors.As(err, &dbErr) { // Обработка конкретной ошибки MySQL с доступом к коду ошибки fmt.Printf("Код ошибки БД: %d\n", dbErr.Number) }
Заключение
Выбор между errors.Is и errors.As зависит от задачи:
- Используйте
errors.Is, когда вам нужно обнаружить конкретную ошибку (например, для проверки наio.EOFилиcontext.Canceled). - Используйте
errors.As, когда вам нужно извлечь и работать с ошибкой определённого типа, чтобы получить доступ к дополнительным данным (полям, методам).
Правильное использование этих функций делает обработку ошибок в Go более чистой, типобезопасной и удобной для работы с обёрнутыми ошибками.