Подтвердить что ты не робот

Как сравнить ошибки Go

У меня есть значение ошибки, которое при печати на консоли дает мне Token is expired

Как я могу сравнить это с определенным значением ошибки? Я попробовал это, но это не сработало:

if err == errors.New("Token is expired") {
      log.Printf("Unauthorised: %s\n", err)
}
4b9b3361

Ответ 1

Попробуйте

err.Error() == "Token is expired"

Или создайте собственную ошибку, выполнив интерфейс ошибки.

Ответ 2

Этот ответ для Go 1.12 и более ранних версий.

Определите значение ошибки в библиотеке

package fruits

var NoMorePumpkins = errors.New("No more pumpkins")

Не создавайте ошибок с errors.New где-либо в коде, но возвращайте предопределенное значение всякий раз, когда возникает ошибка, и тогда вы можете сделать следующее:

package shop

if err == fruits.NoMorePumpkins {
     ...
}

См. io об ошибках пакета для справки.

Это можно улучшить, добавив методы, чтобы скрыть реализацию проверки и сделать клиентский код более защищенным от изменений в пакете fruits.

package fruits

func IsNoMorePumpkins(err error) bool {
    return err == NoMorePumpkins
} 

Смотрите os ошибки пакета для справки.

Ответ 3

Для пакетов идиоматично экспортировать переменные ошибок, которые они используют, чтобы другие могли сравнивать их.

Например, если ошибка возникла из пакета с именем myPkg и была определена как:

var ErrTokenExpired error = errors.New("Token is expired")

Вы можете сравнить ошибки непосредственно как:

if err == myPkg.ErrTokenExpired {
    log.Printf("Unauthorised: %s\n", err)
}

Если ошибки происходят из стороннего пакета и в нем не используются экспортированные переменные ошибок, то вы можете просто сравнить со строкой, полученной из err.Error(), но будьте осторожны с этим подходом, поскольку изменение строки ошибки может не будет выпущен в основной версии и нарушит вашу бизнес-логику.

Ответ 4

Тип ошибки - это тип интерфейса. Переменная error представляет любое значение, которое может быть описано как строка. Вот объявление интерфейса:

type error interface {
    Error() string
}

Наиболее часто используемая реализация ошибок - это пакет ошибок, не экспортируемый тип errorString:

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

Посмотрите этот вывод рабочего кода (The Go Playground):

package main

import (
    "errors"
    "fmt"
    "io"
)

func main() {
    err1 := fmt.Errorf("Error")
    err2 := errors.New("Error")
    err3 := io.EOF

    fmt.Println(err1)         //Error
    fmt.Printf("%#v\n", err1) // &errors.errorString{s:"Error"}
    fmt.Printf("%#v\n", err2) // &errors.errorString{s:"Error"}
    fmt.Printf("%#v\n", err3) // &errors.errorString{s:"EOF"}
}

выход:

Error
&errors.errorString{s:"Error"}
&errors.errorString{s:"Error"}
&errors.errorString{s:"EOF"}

Также см.: Операторы сравнения

Операторы сравнения сравнивают два операнда и выдают нетипизированное логическое значение. В любом сравнении первый операнд должен быть назначен типу второго операнда или наоборот.

Операторы равенства == и != Применяются к операндам, которые сравнимы.

Значения указателя сопоставимы. Два значения указателя равны, если они указывают на одну и ту же переменную или если оба имеют значение nil. Указатели на различные переменные нулевого размера могут совпадать или не совпадать.

Значения интерфейса сопоставимы. Два значения интерфейса равны, если они имеют идентичные динамические типы и одинаковые динамические значения или оба имеют значение nil.

Значение x неинтерфейсного типа X и значение t интерфейсного типа T сравнимы, когда значения типа X сравнимы и X реализует T. Они равны, если динамический тип t идентичен X, а динамическое значение t равно x,

Значения структур сравнимы, если все их поля сопоставимы. Два значения структуры равны, если их соответствующие непустые поля равны.


Так:

1- Вы можете использовать Error(), как этот рабочий код (The Go Playground):

package main

import (
    "errors"
    "fmt"
)

func main() {
    err1 := errors.New("Token is expired")
    err2 := errors.New("Token is expired")
    if err1.Error() == err2.Error() {
        fmt.Println(err1.Error() == err2.Error()) // true
    }
}

выход:

true

2- Также вы можете сравнить его с nil, как этот рабочий код (The Go Playground):

package main

import (
    "errors"
    "fmt"
)

func main() {
    err1 := errors.New("Token is expired")
    err2 := errors.New("Token is expired")
    if err1 != nil {
        fmt.Println(err1 == err2) // false
    }
}

выход:

false

3- Также вы можете сравнить его с точно такой же ошибкой, как этот рабочий код
(The Go Playground):

package main

import (
    "fmt"
    "io"
)

func main() {
    err1 := io.EOF
    if err1 == io.EOF {
        fmt.Println("err1 is : ", err1)
    }
}

выход:

err1 is :  EOF

ссылка: https://blog.golang.org/error-handling-and-go

Ответ 5

Объявление ошибки и сравнение ее с "==" (как в err == myPkg.ErrTokenExpired) больше не является лучшей практикой для Go 1.13 (Q3 2019)

Примечания к выпуску упоминают:

Go 1.13 contains support for error wrapping, as first proposed in the Error Values proposal и discussed on the associated issue.

Ошибка e может обернуть другую ошибку w, предоставив метод Unwrap, который возвращает w.
Оба e и w доступны для программ, что позволяет e предоставлять дополнительный контекст для w или интерпретировать его, в то же время позволяя программам принимать решения на основе w.

To support wrapping, fmt.Errorf now has a %w verb for creating wrapped errors, и three new functions in the errors package (errors.Unwrap, errors.Is и errors.As) simplify unwrapping и inspecting wrapped errors.

Итак, FAQ по значению ошибки объясняет:

Вы должны быть готовы к тому, что ошибки, которые вы получаете, могут быть перенесены.

Если вы в настоящее время сравниваете ошибки, используя ==, используйте вместо этого errors.Is.
Пример:

if err == io.ErrUnexpectedEOF

становится

if errors.Is(err, io.ErrUnexpectedEOF)
  • Проверяет форму, если err != nil не нужно менять.
  • Сравнения с io.EOF не нужно менять, потому что io.EOF никогда не следует переносить.

Если вы проверяете тип ошибки, используя утверждение типа или переключатель типа, используйте вместо этого errors.As. Пример:

if e, ok := err.(*os.PathError); ok

становится

var e *os.PathError
if errors.As(err, &e)

Также используйте этот шаблон, чтобы проверить, реализует ли ошибка интерфейс. (Это один из тех редких случаев, когда указатель на интерфейс уместен.)

Перепишите переключатель типа как последовательность if-elses.