简介

相关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

demo链接

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

  1. 如果只有一个%w,那么errors.Unwrap会有输出被wrap的error,比如上面的 err0
    1. 但是如果这个%werrors.Join后的结果(如上的err3),那么会输出的不是 err3,而是被Join的第一个error,也就是err1.
  2. 如果有多个%w,那么errors.Unwrap会输出nil

errors.Join

error的层级结构如下 Go error layers

但实际上errors.Unwrap(err3)输出是nil,errors.Unwrap(err4)输出是err1。我们通过errors.As或者errors.Iserr4进行检查,会发现它是包含err1err2err3的。

    ...
    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/