How to compare Go errors

2020-01-30 08:11发布

I have an error value which when printed on console gives me Token is expired

How can I compare it with a specific error value? I tried this but it did not work:

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

标签: go
5条回答
Bombasti
2楼-- · 2020-01-30 08:47

Declaring an error, and comparing it with '==' (as in err == myPkg.ErrTokenExpired) is no longer the best practice with Go 1.13 (Q3 2019)

The release notes mentions:

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

An error e can wrap another error w by providing an Unwrap method that returns w.
Both e and w are available to programs, allowing e to provide additional context to w or to reinterpret it while still allowing programs to make decisions based on w.

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

So the Error Value FAQ explains:

You need to be prepared that errors you get may be wrapped.

If you currently compare errors using ==, use errors.Is instead.
Example:

if err == io.ErrUnexpectedEOF

becomes

if errors.Is(err, io.ErrUnexpectedEOF)
  • Checks of the form if err != nil need not be changed.
  • Comparisons to io.EOF need not be changed, because io.EOF should never be wrapped.

If you check for an error type using a type assertion or type switch, use errors.As instead. Example:

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

becomes

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

Also use this pattern to check whether an error implements an interface. (This is one of those rare cases when a pointer to an interface is appropriate.)

Rewrite a type switch as a sequence of if-elses.

查看更多
唯我独甜
3楼-- · 2020-01-30 08:53

This answer is for Go 1.12 and earlier releases.

Define an error value in a library

package fruits

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

Do not create errors with errors.New anywhere in the code but return the predefined value whenever error occurs and then you can do the following:

package shop

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

See io package errors for reference.

This can be improved by adding methods to hide the check implementation and make the client code more immune to changes in fruits package.

package fruits

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

See os package errors for reference.

查看更多
贼婆χ
4楼-- · 2020-01-30 08:56

It's idiomatic for packages to export error variables that they use so others can compare against them.

E.g. If an error would came from a package named myPkg and was defined as:

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

You could compare the errors directly as:

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

If the errors come from a third party package and that doesn't use exported error variables then what you can do is simply to compare against the string you get from err.Error() but be careful with this approach as changing an Error string might not be released in a major version and would break your business logic.

查看更多
5楼-- · 2020-01-30 08:58

Try

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

Or create your own error by implementing the error interface.

查看更多
够拽才男人
6楼-- · 2020-01-30 09:00

The error type is an interface type. An error variable represents any value that can describe itself as a string. Here is the interface's declaration:

type error interface {
    Error() string
}

The most commonly-used error implementation is the errors package's unexported errorString type:

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

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

See this working code output (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"}
}

output:

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

Also see: Comparison operators

Comparison operators compare two operands and yield an untyped boolean value. In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

The equality operators == and != apply to operands that are comparable.

Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.

Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.


So:

1- You may use Error(), like this working code (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
    }
}

output:

true

2- Also you may compare it with nil, like this working code (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
    }
}

output:

false

3- Also you may compare it with exact same error, like this working code
(The Go Playground):

package main

import (
    "fmt"
    "io"
)

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

output:

err1 is :  EOF

ref: https://blog.golang.org/error-handling-and-go

查看更多
登录 后发表回答