go 的错误处理 error
2021-12-12 本文已影响0人
wayyyy
error 接口
type error interface {
Error() string
}
error 是一种内建的接口类型,不需要引用 任何包就可以直接使用。error 接口只声明了一个Error
方法,任何实现了该方法的结构体都可以作为 error 来使用。
标准库errors 包中的 errorString 就是实现了 error 接口的一个例子:
type errorString struct {
a string
}
func (e *errorString) Error() string {
return e.s
}
异常处理
针对 error 而言,异常处理包括如何检查错误,如何传递错误。
-
检查error
最常见的错误是和nil
值比较, -
传递error
在一个函数中收到一个error,往往需要附加一些上下文信息再把 error 继续向上层抛。
最常见的添加附加上下文信息的方法是使用fmt.Errorf()
if err != nil { return fmt.Errorf("decompress %v: %v", name, err) }
这种方式抛 error 有一个糟糕的问题,那就是原 error 信息和附加的信息被糅合在一起了。
为了解决这一问题,Go 1.13 中引入了对error的优化,最核心的内容就是引入了 wrapError 这一新的 error 类型。
-
wrapError
type wrapError struct { msg string err error } func (e *wrapError ) Error() string { return e.msg } func (e *wrapError ) Unwarp() error { return e.err }
这样多个error 在函数之间传递,error 就被组织成了一个链式结构:
image.png -
fmt.Errorf()
在go 1.13 中,fmt.Errorf() 新增了格式动词%w
用于生成wrapError
实例。func main() { err := errors.New("this is first error") warpErr1 := fmt.Errorf("this is second error: %w", err) warpErr2 := fmt.Errorf("this is thrid error: %w", warpErr1) fmt.Println(warpErr2) } // 输出: this is thrid error: this is second error: this is first error
使用fmt.Errorf 有2点需要注意:
- 每次只接受一个
%w
-
%w
只能用于匹配 error 参数
- 每次只接受一个
-
errors.Unwrap
Unwrap 则是解开 wrap,返回成员errorunWarpErr2 := errors.Unwrap(warpErr2) fmt.Println(unWarpErr2) // 输出: this is second error: this is first error
其实现源码实现如下:
func Unwrap(err error) error { // 检查是否实现了 Unwrap 函数 // 如果没有实现 Unwrap 函数,则不支持 Unwrap u, ok := err.(interface { Unwrap() error }) if !ok { return nil } return u.Unwrap() }
-
errors.ls
errors.ls() 用于检查特定的 error 链中是否包含了指定的 error 值。比如:其源码实现可能会是:
func Is(err, target error) bool { for { if err == target { return true } if x, ok := err.(interface { Is(error) bool }); ok && x.Is(target) { return true } if err = Unwrap(err); err == nil { return false } } }
-
errors.As
erross.As 用于从一个 error链中查找是否有指定的类型出现,如有,则把 error转换成该类型