fmt.Errorf使用with%w和 using包装错误之间似乎存在不一致errors.Wrap:
e1 := errors.New("error1")
efmt := fmt.Errorf("error2: %w", e1)
eerr := errors.Wrap(e1, "error2")
fmt.Println(errors.Unwrap(efmt)) // error1
fmt.Println(errors.Unwrap(efmt) == e1) // true
fmt.Println(errors.Unwrap(eerr)) // error2: error1
fmt.Println(errors.Unwrap(eerr) == e1) // false :-(
Run Code Online (Sandbox Code Playgroud)
完整的例子可以在这里找到
我不确定这是否是有意为之,但这似乎不一致......有什么原因吗?这有记录在任何地方吗?
这是预期的工作,并且不违反文档。多个错误可能会包装在一个error值中,并且由于调用Unwrap()返回一个错误,显然没有得到您期望的结果并不意味着预期的错误没有被包装。
该errors包来自标准库。它没有errors.Wrap()功能。您使用的来自github.com/pkg/errors.Wrap(),它在引擎盖下进行“双重包装”。
首先,它用给定的错误消息包装错误,然后再次包装以保留堆栈信息:
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
err = &withMessage{
cause: err,
msg: message,
}
return &withStack{
err,
callers(),
}
}
Run Code Online (Sandbox Code Playgroud)
当您调用 时Unwrap(),将返回第二次包装的错误(这不是原始错误,而是包装原始错误的包装错误),Unwrap()再次调用将返回原始错误。
fmt.Println("Double unwrap:",
errors.Unwrap(errors.Unwrap(err2wrp)) == err1)
Run Code Online (Sandbox Code Playgroud)
这就是为什么你应该使用errors.Is()以避免此类“怪癖”:
fmt.Println("Proper use:", errors.Is(err2wrp, err1))
Run Code Online (Sandbox Code Playgroud)
在Go Playground上尝试这些。
请注意,上面的报告true是您调用github.com/pkg/errors.Is()还是标准库的errors.Is().