如何比较Golang错误对象

aaj*_*aaj 37 go

我有一个错误对象,当在控制台上打印给我 Token is expired

如何将其与特定错误值进行比较.我尝试了这个,但没有奏效.

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

Von*_*onC 38

==err == myPkg.ErrTokenExpiredGo 1.13(2019 年第 3 季度)中声明错误并将其与 ' '(如)进行比较不再是最佳实践

发行说明中提到:

Go 1.13 包含对错误包装的支持,正如错误值提案中首次提出的那样,并在相关问题上进行了讨论

一个错误e可以w通过提供一个Unwrap返回的方法来包装另一个错误w
双方ew可供程序,允许e以提供额外的上下文w或重新解释它同时还允许根据程序做出判断w

为了支持包装,fmt.Errorf现在有一个%w用于创建包装错误的动词,并且errors包中的三个新函数( errors.Unwrap,errors.Iserrors.As) 简化了展开和检查包装错误。

所以错误值常见问题解释了:

你需要准备好你得到的错误可能会被包装。

如果您当前使用 比较错误==,请errors.Is改用。
例子:

if err == io.ErrUnexpectedEOF
Run Code Online (Sandbox Code Playgroud)

变成

if errors.Is(err, io.ErrUnexpectedEOF)
Run Code Online (Sandbox Code Playgroud)
  • 如果err != nil不需要更改,则检查表格。
  • 比较io.EOF不需要改变,因为io.EOF不应该被包装。

如果您使用类型断言类型 switch检查错误类型,请errors.As改用。例子:

if e, ok := err.(*os.PathError); ok
Run Code Online (Sandbox Code Playgroud)

变成

var e *os.PathError
if errors.As(err, &e)
Run Code Online (Sandbox Code Playgroud)

还可以使用此模式检查错误是否实现了接口。(这是指向接口的指针合适的罕见情况之一。)

将类型开关重写为if-elses.

  • 百万名誉你真是当之无愧! (3认同)

Grz*_*Żur 30

在库中定义错误值

package fruits

var NoMorePumpkins = errors.New("No more pumpkins")
Run Code Online (Sandbox Code Playgroud)

不要errors.New在代码中的任何位置创建错误,而是在发生错误时返回预定义的值,然后您可以执行以下操作:

package shop

if err == fruits.NoMorePumpkins {
     ...
}
Run Code Online (Sandbox Code Playgroud)

请参阅io包装错误以供参考.

  • 实际问题不是有人可能会覆盖 `fruits.NoMorePumpkins`,而是他们可能*包装*它,并且 `==` 会失败。 (4认同)

Sri*_*har 28

尝试

err.Error() == "Token is expired"
Run Code Online (Sandbox Code Playgroud)

或者通过实现错误接口来创建自己的错误.

  • 我不知道为什么我的评论总是被删除!这个答案不是建议,所以不能是“糟糕的建议”。问题本身有字符串文字的比较。我试图回答这个问题,而不是提供最佳实践的建议。 (4认同)
  • 这是可怕的建议。如果错误文本更改了怎么办?您应该将其与错误“ var”进行比较。 (3认同)
  • 这不是建议。 (2认同)

jus*_*ius 16

包导出它们使用的错误变量以便其他人可以与它们进行比较,这是惯用的.

例如,如果错误来自名为myPkg的包,并且定义为:

var ErrTokenExpired error = errors.New("Token is expired")
Run Code Online (Sandbox Code Playgroud)

您可以将错误直接比较为:

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

如果错误来自第三方软件包并且不使用导出的错误变量,那么您可以做的只是与从err.Error()获得的字符串进行比较,但要小心这种方法,因为更改错误字符串可能不会在主要版本中发布,并会破坏您的业务逻辑.

  • 比比较字符串要好得多。 (2认同)

小智 12

错误类型是接口类型.错误变量表示可以将自身描述为字符串的任何值.这是接口的声明:

type error interface {
    Error() string
}
Run Code Online (Sandbox Code Playgroud)

最常用的错误实现是错误包的未导出错误字符串类型:

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

func (e *errorString) Error() string {
    return e.s
}
Run Code Online (Sandbox Code Playgroud)

看到这个工作代码输出(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"}
}
Run Code Online (Sandbox Code Playgroud)

输出:

Error
&errors.errorString{s:"Error"}
&errors.errorString{s:"Error"}
&errors.errorString{s:"EOF"}
Run Code Online (Sandbox Code Playgroud)

另见:比较运算符

比较运算符比较两个操作数并产生无类型的布尔值.在任何比较中,第一个操作数必须可以分配给第二个操作数的类型,反之亦然.

等于运算符==!=适用于可比较的操作数.

指针值具有可比性.如果两个指针值指向同一个变量或两者的值都为nil,则它们相等.指向不同零大小变量的指针可能相同也可能不相等.

接口值具有可比性.如果两个接口值具有相同的动态类型和相等的动态值,或者两者的值都为nil,则它们相等.

当类型X的值可比较且X实现T时,非接口类型X的值x和接口类型T的值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
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

true
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

false
Run Code Online (Sandbox Code Playgroud)

3-此外,您可以将其与完全相同的错误进行比较,例如此工作代码
(The Go Playground):

package main

import (
    "fmt"
    "io"
)

func main() {
    err1 := io.EOF
    if err1 == io.EOF {
        fmt.Println("err1 is : ", err1)
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

err1 is :  EOF
Run Code Online (Sandbox Code Playgroud)

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


wst*_*wst 7

不鼓励按字符串比较错误。相反,您应该按值比较错误。

package main

import "errors"

var NotFound = errors.New("not found")

func main() {
    if err := doSomething(); errors.Is(err, NotFound) {
        println(err)
    }
}

func doSomething() error {
    return NotFound
}
Run Code Online (Sandbox Code Playgroud)

如果您是库作者并希望导出错误,则它特别有用,以便用户可以对不同类型的错误采取不同的行动。标准库也这样做。

这种方法的问题在于任何人都可以更改导出的值,因为 Go 不支持不可变值。但是,没有什么可以阻止您将字符串用作错误并将其设为const.

package main

type CustomError string

func (ce CustomError) Error() string {
    return string(ce)
}

const NotFound CustomError = "not found"

func main() {
    if err := doSomething(); errors.Is(err, NotFound) {
        println(err)
    }
}

func doSomething() error {
    return NotFound
}
Run Code Online (Sandbox Code Playgroud)

这是更冗长但更安全的方法。


Moh*_*por 5

您应该首先考虑按值比较错误,如其他解决方案中所述:

if errors.Is(err1, err2) {
  // do sth
}
Run Code Online (Sandbox Code Playgroud)

然而,在某些情况下,从函数返回的错误有点复杂,例如,错误被多次包装,并且在每个函数调用中都添加了上下文,例如 ,fmt.Errorf("some context: %w", err)您可能只想比较两个错误消息错误。在这种情况下,您可以这样做:

// SameErrorMessage checks whether two errors have the same messages.
func SameErrorMessage(err, target error) bool {
    if target == nil || err == nil {
        return err == target
    }
    return err.Error() == target.Error()
}

func main() {
  ...
  if SameErrorMessage(err1, err2) {
     // do sth
  }

}
Run Code Online (Sandbox Code Playgroud)

请注意,如果您只是使用

if err1.Error() == err2.Error() {
  // do sth
}
Run Code Online (Sandbox Code Playgroud)

如果是err1或 ,您可能会面临 nil 指针取消引用运行时错误。err2nil