Golang1.20新特性 multi errors
目录
简介⌗
相关issue: https://github.com/golang/go/issues/53435#issuecomment-1191752789
增加了两个接口
// 匿名接口 实现见 https://go-review.googlesource.com/c/go/+/432898/11/src/errors/wrap.go
interface{
Unwrap() []error
}
// 实现见 https://go-review.googlesource.com/c/go/+/432898/11/src/errors/join.go
func Join(errs ...error) error
增强了fmt.Errorf
和函数语义,可以支持多个%w
了,实现见https://go-review.googlesource.com/c/go/+/432898/11/src/fmt/errors.go 。
Demo Code⌗
func main() {
err0 := errors.New("err0")
wErr0 := fmt.Errorf("werr0: %w", err0)
fmt.Printf("errors.Unwrap before 1.20(fmt.Errorf): %v\n", wErr0)
// 输出
// errors.Unwrap before 1.20(fmt.Errorf): werr0: err0
// 创建两个error
err1 := errors.New("err1")
err2 := errors.New("err2")
// Join 函数将多个错误连接起
err3 := errors.Join(err1, err2)
fmt.Printf("output err3:\n%v\n", err3)
// 输出
// err1
// err2
if errors.Is(err3, err1) && errors.Is(err3, err2) {
fmt.Println("err3 contains err1 and err2")
}
fmt.Printf("errors.Unwrap err3(Join):%v\n", errors.Unwrap(err3))
// 输出
// errors.Unwrap err3(Join):<nil>
err4 := fmt.Errorf("err4 wrap err3\n%w", err3)
fmt.Printf("output err4:\n%v\n", err4)
// 输出
// err4 wrap err3
// err1
// err2
if errors.Is(err4, err1) && errors.Is(err4, err2) {
fmt.Println("err4 contains err1 and err2")
}
if errors.Is(err4, err3) {
fmt.Println("err4 contains err3")
}
fmt.Printf("errors.Unwrap err4(fmt.Errorf single %cw):%v\n", '%', errors.Unwrap(err4))
// 输出
// errors.Unwrap err4(fmt.Errorf single %w):err1
err5 := errors.New("err5")
err6 := errors.Join(err4, err5)
fmt.Printf("output err6:\n%v\n", err6)
// 输出
// err4 wrap err3
// err1
// err2
// err5
if errors.Is(err6, err1) && errors.Is(err6, err2) {
fmt.Println("err6 contains err1 and err2")
}
if errors.Is(err6, err3) {
fmt.Println("err6 contains err3")
}
if errors.Is(err6, err4) {
fmt.Println("err6 contains err4")
}
if errors.Is(err6, err5) {
fmt.Println("err6 contains err5")
}
// fmt.Errorf multiple errors
err7 := fmt.Errorf("%w\n%w", err4, err5)
fmt.Printf("output err7:\n%v\n", err7)
fmt.Printf("errors.Unwrap err7(fmt.Errorf multiple %cw):%v\n", '%', errors.Unwrap(err7))
// 输出
// errors.Unwrap err7(fmt.Errorf multiple %w):<nil>
}
需要注意的地方⌗
fmt.Errorf⌗
err0 := errors.New("err0")
wErr0 := fmt.Errorf("werr0: %w", err0)
fmt.Printf("errors.Unwrap before 1.20(fmt.Errorf): %v\n", wErr0)
// 输出
// errors.Unwrap before 1.20(fmt.Errorf): werr0: err0
err1 := errors.New("err1")
err2 := errors.New("err2")
// Join 函数将多个错误连接起
err3 := errors.Join(err1, err2)
err4 := fmt.Errorf("err4 wrap err3\n%w", err3)
fmt.Printf("errors.Unwrap err4(fmt.Errorf single %cw):%v\n", '%', errors.Unwrap(err4))
// 输出
// errors.Unwrap err4(fmt.Errorf single %w):err1
...
...
// fmt.Errorf multiple errors
err7 := fmt.Errorf("%w\n%w", err4, err5)
fmt.Printf("errors.Unwrap err7(fmt.Errorf multiple %cw):%v\n", '%', errors.Unwrap(err7))
// 输出
// errors.Unwrap err7(fmt.Errorf multiple %w):<nil>
fmt.Errorf
:
- 如果只有一个
%w
,那么errors.Unwrap
会有输出被wrap的error,比如上面的err0
,- 但是如果这个
%w
是errors.Join
后的结果(如上的err3
),那么会输出的不是err3
,而是被Join
的第一个error,也就是err1
.
- 但是如果这个
- 如果有多个
%w
,那么errors.Unwrap
会输出nil
errors.Join⌗
error的层级结构如下
但实际上errors.Unwrap(err3)
输出是nil,errors.Unwrap(err4)
输出是err1
。我们通过errors.As
或者errors.Is
对err4
进行检查,会发现它是包含err1
、err2
、err3
的。
...
err1 := errors.New("err1")
err2 := errors.New("err2")
err3 := errors.Join(err1, err2)
fmt.Printf("errors.Unwrap err3(Join):%v\n", errors.Unwrap(err3))
// 输出
// errors.Unwrap err3(Join):<nil>
err4 := fmt.Errorf("err4 wrap err3\n%w", err3)
if errors.Is(err4, err1) && errors.Is(err4, err2) {
fmt.Println("err4 contains err1 and err2")
}
if errors.Is(err4, err3) {
fmt.Println("err4 contains err3")
}
...
更多参考⌗
https://lukas.zapletalovi.com/posts/2022/wrapping-multiple-errors/
Read other posts