Go1.13 Error新特征

即将发布的Go1.13对errors包进行了增强,新特征主要来自提案: Proposal: Go 2 Error Inspection

为了使error为程序员和程序提供足够多的相关信息,Go1.13对error处理的更新如下:

  • 定义Wrapper接口(其实更应该称为Unwrapper[1] ,用于Unwrap操作。
  • errors包新增了func Is(err, target error) boolfunc As(err error, target interface{}) bool函数,用于比较和断言error。
  • 定义了func Unwrap(err error) error用于快速Unwrap错误。
  • fmt包的Errorf方法修改了实现,增加新的verb%w%w只接受error类型的参数,用于快速Wrap错误。

包装错误

err := errors.New("my error")
err = fmt.Errorf("1s wrapping my error with Errorf: %w", err)
err = fmt.Errorf("2nd wrapping my error with Errorf: %w", err)

错误的解包

err = errors.Unwrap(err)

func Unwrap(err error) error间接调用err的Unwrap() error方法。

错误比较

if errors.Is(err,os.PathError) {
	//Do Something
}

Is函数会逐层调用Unwrap函数并比较error链上的所有error是否与target匹配,直到匹配targer或者Unwrap返回nil。匹配target的条件:Unwrap返回值与target相同(==)或者Unwrap的返回值实现了Is(error) bool方法,调用Is(target)返回true

错误断言

if errors.As(err,&target){
	//Do Something
}

As函数会逐层调用Unwrap函数并比较error链上的所有error是否与target匹配,如果匹配,则把匹配到的值设置到target并回返true
如果target不是指向error类型或者interface{}类型的指针,则会panic,如果errnil,返回false

示例

package main

import (
	"errors"
	"fmt"
)

//BaseError is a Custom error
type BaseError struct {
	msg string
}

func (b BaseError) Error() string {
	return fmt.Sprintf("BaseError Message : %s", b.msg)
}

var baseError = BaseError{msg: "Base"}

func main() {
	err := fmt.Errorf("Wrap 001 : %w", baseError)
	err = fmt.Errorf("Wrap 002 : %w", err)
	fmt.Println(err)

	err = errors.Unwrap(err)
	fmt.Println(err)

	if errors.Is(err, baseError) {
		fmt.Println("err Is baseError")
	}

	var anotherError BaseError
	if errors.As(err, &anotherError) {
		fmt.Printf("%v\n", anotherError)
	}
}

执行结果:

$ go run main.go
Wrap 002 : Wrap 001 : BaseError Message : Base
Wrap 001 : BaseError Message : Base
err Is baseError
BaseError Message : Base

建议

仅包装来自公共函数或方法的错误。否则就直接传播错误。

参考链接

When to wrap errors


[1] : 写这篇文章时,Go1.13版本还没有正式release,github仓库RC1分支的代码中没有Wrapper接口的定义,只是在Unwrap方法中引用了匿名的interface 参见
之所以称之为Wrapper接口是因为 https://github.com/golang/xerrors/blob/master/wrap.go#L12 中定义的接口是Wrapper