go1.13之Error Warp
本文挑重点来看go1.13版本中对于错误处理部分提供的新功能(Error wrapping)(proposal参考资料1)。
提案中对于error新功能主要分两点:
- error可以包裹着其他error。而不是以前的做法,以字符串拼接方式往上传递。
- 使用
%+v
打印error时,带有堆栈信息,精确到函数名与行号。
其实这两块功能,很早前Dave Cheney就在一篇博文中论述过他对error处理的理解,并给出相关的开源包github.com/pkg/errors
(这个库现在也处于维护状态,不再接受新功能)。
用过的同学应该比较熟悉,它解决的问题也是上面提到两点error新功能。
功能1
go1.13之前,方法内部逻辑中,遇到其它方法返回error时,一种粗暴的处理方式,直接return fmt.Errorf("operate failed %v", err)
。这种做法问题是把err转换成为另一个字符串,原始的err被抹掉。
如果想添加额外的错误信息,又不想抹掉原始的err,可以封装一个struct,上层通过err.Err.(type)
的方式来检查,但显然加大编码复杂度。
而用了go1.13之后,解决这个问题的方案就变成下面这样
|
|
这样原始的err不会变抹掉,通过errors.Is()
方法可以检查出来。其次通过fmt.Println()
输出是仍是字符串,样式与之前使用%v`时相比较没有改变。
还可以通过errors.As()
方法将对应的原始err提取出来。
所以用户在升级新版本后,有两个地方的代码需要转换下
|
|
用了这两种做法,即使API提供方后面更改返回的error含义,兼容成本较低。
基于go1.13之前不同error不同的使用场景,官方写了FAQ,参考资料3。
功能2
打印error时带上堆栈信息功能很实用,之前遇到error时,排查都需要顺藤摸瓜的找到源头,比较浪费时间。
坏消息,功能2在go1.13官方标准库中被腰斩了,推迟到go1.14。详细原因可参考资料2。
好消息是官方提供了golang.org/x/xerrors
。这个包完整实现了这两个新功能点,主要生产环境不易升级go版本的用户,可以尝鲜,或者是对已经升级为新版本的下游做兼容。
看一下使用xerrors
包打印error时带堆栈的效果。
|
|
注意,xerrors.Errorf("foo1 : %w",foo())
中必须以: %w
的格式占位,否则不起作用。
资料1:https://go.googlesource.com/proposal/+/master/design/29934-error-values.md
资料2:https://github.com/golang/go/issues/29934#issuecomment-489682919