← Назад к вопросам

Чем отличается errors.Is от errors.As?

2.0 Middle🔥 161 комментариев
#Основы Go

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отличие 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)
    }
}

Как работает:

  1. Сравнивает err с target через ==.
  2. Если err реализует метод Is(error) bool, вызывает его.
  3. Если ошибка обёрнута (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
}

Как работает:

  1. Проверяет, можно ли присвоить err (или ошибку из цепочки) в переменную target.
  2. Если да — присваивает и возвращает true.
  3. Также рекурсивно проходит по цепочке обёрнутых ошибок.

Сравнительная таблица

Критерийerrors.Iserrors.As
Основная цельПроверить, есть ли ошибка-образец в цепочкеИзвлечь ошибку конкретного типа из цепочки
МеханизмСравнение значений (через == или метод Is())Проверка присваиваемости типов
Возвращаемое значениеbool (найдена или нет)bool + присваивание в target
Аргумент targeterror (обычно синглтон)Указатель на тип ошибки (например, *MyError)
Типичный use-caseif errors.Is(err, io.EOF)if errors.As(err, &pathError)

Важные нюансы

  1. Порядок аргументов: Оба метода принимают err первым аргументом, но target разный.
  2. Цепочка ошибок: Оба метода рекурсивно разворачивают цепочку, созданную через fmt.Errorf с %w или кастомные обёртки.
  3. Производительность: errors.Is обычно легче, так как сравнивает значения, а errors.As использует рефлексию (reflect) для проверки типов.
  4. Совместное использование: Часто применяются вместе:
    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 более чистой, типобезопасной и удобной для работы с обёрнутыми ошибками.

Чем отличается errors.Is от errors.As? | PrepBro