命令记不住

登陆

error 版本工程迁移

error版本工程迁移

老版本开发的go程序如果想使用链式error,那么需要对旧版本工程做一些适配

  • 创建error时,fmt.Errorf()格式化动词由%v修改为%w
  • 等值(==)检查可以使用errors.Is()替代
  • 类型断言可以使用errors.As()替代
  • 自定义类型额外实现Unwrap()方法
  • 自定义类型额外实现As()方法
  • 自定义类型额外实现Is()方法

1.传递error

旧工程使用%v动词来组装error并传递,迁移时可以直接改为%w动词

if err != nil {
    return  fmt.Errorf("decompress %v: %w",name, err)
}

虽然旧工程中返回的是errors.errorString类型,而新工程中返回的是fmt.wrapError,但不影响上游使用,fmt.wrapError完全兼容errors.errorString

2.等值检查

等值检查可以直接使用errors.Is()替换

if errors.Is(err, os.ErrPermission) {
    //permission denied
}

由于errors.Is()会先拿err与目标值进行比较,不相等才会逐层拆解err并与目标值进行比较,所以使用errors.Is()没有副作用,即便err不是链式error

3.类型断言

类型断言替换成errors.As()时需要先声明一个目标类型的指针变量

var target *os.PathError
if errors.As(err,&target){
    fmt.Printf("it's an os.PaathError, operation %s, path: %s, msg: %v\n",target.Op,target.Path,target.Err)
}

4.自定义类型

对于自定义类型,可以参考wrapError的实现,增加Unwrap()函数

type wrapError struct {
        msg  string
        err error
}
func (e *wrapError) Error() string {
        return  e.msg
}
func (e *wrapError) Unwrap() error{
        return e.err
}

此外,自定义类型还可以实现自己的Is和As接口:

Is(error) bool
As(interface{}) bool

在实际场景中,几乎不需要实现这两个接口

推荐阅读