1、errors 包
底层实现
// 工厂方法,创建错误// 可以推断 error 是一个接口://// type error interface {// Error() string// }//func New(text string) error {return errorString{text}}// errorString 结构体实现了 error 接口type errorString struct {s string}// 该结构体只有一个方法,返回错误字符串func (e *errorString) Error() string {return e.s}
error 包的缺陷
缺陷一:无调用栈信息
var (AssertError = errors.New("assert error"))// 直接返回错误func f() error {return AssertError}// c() 调用 f()func c() error {// golang 一般错误处理方式err := f()if err != nil {return err}fmt.Println("do somthing")return nil}func main() {err := c()if err != nil {fmt.Printf("main invoke c(): %+v\n", err)}}
main invoke c(): assert error
缺陷二:添加上下文信息错误发生变化
func f() error {return AssertError}func c() error {err := f()if err != nil {// 错误中添加上下文信息return fmt.Errorf("c invoke f err, %v", err)}fmt.Println("do somthing")return nil}func main() {err := c()// 如果是 AssertError,则一种处理方式// 其它错误走额外的分支if err == AssertError {fmt.Println("main invoke c(): AssertError\n")} else {fmt.Printf("unknown err: %+v\n", err)}}
unknown err: c invoke f err, assert error
2、开源错误处理推荐
github.com/pkg/errors 在原来 go 内置 error 基础上增加了栈信息和上下文信息,很好的弥补了上面内置 error 的缺陷。
func f() error {// Wrap 在 error 基础上添加调用栈信息,同时添加额外的// 用户上下文信息,如果err是nil,则返回的是nilreturn pkgerr.Wrap(AssertError, "inner f err")// WithStack 只添加调用栈信息//return pkgerr.WithStack(AssertError)}func c() error {err := f()if err != nil {// WithMessage 仅仅添加上下文信息return pkgerr.WithMessage(err, "c invoke f")}fmt.Println("do somthing")return nil}func main() {err := c()if err != AssertError {fmt.Printf("main invoke c()--------------------->: %+v\n", err)}}
main invoke c()--------------------->: assert errorinner f errmain.fUsers/dkos/t/newquick.go:17main.cUsers/dkos/t/newquick.go:25main.mainUsers/dkos/t/newquick.go:36runtime.mainusr/local/go/src/runtime/proc.go:225runtime.goexitusr/local/go/src/runtime/asm_amd64.s:1371c invoke f
上面输出了完整的调用栈,同时错误类型没有改变。我们再看看主要的几个函数几个函数是如何实现的:
Wrap:包装一个错误,添加上下文和栈信息
func Wrap(err error, message string) error {// 本身没有错误的话,直接返回 nilif err == nil {return nil}// withMessage 实现了 error 接口// 同时提供了其它功能//// cause: 存储错误// msg: 用户要添加的上下文信息err = &withMessage{cause: err,msg: message,}// withStack 继承了(嵌入了) error,// 同时调用底层 runtime.Callers 读取// 调用栈信息return &withStack{err,callers(),}}
WithMessage:Wrap 的简化版本,值添加上下文信息
func WithMessage(err error, message string) error {// 本身没有错误的话,直接返回 nilif err == nil {return nil}// 这里如果 err 被 wrap 的话,那么本身就含有栈信息// 若是 err 没有 Wrap 过,直接添加上下文信息返回return &withMessage{cause: err,msg: message,}}
Cause:获取触发错误的原始错误
func Cause(err error) error {type causer interface {Cause() error}for err != nil {// 判断 err 有无实现 cause 接口// 如果本身没有实现,则 err 本身// 就是触发错误的原始错误cause, ok := err.(causer)if !ok {break}// 如果 err 实现了 cause 接口,// 那么说明它还有底层错误,此时// 循环查询错误链,直到查到没有// 实现 causer 接口的错误,该// 错误就是为经过加工的最原始错误err = cause.Cause()}// 返回原始错误return err}
Is:判断目标错误是否在原始错误链上
func Is(err, target error) bool {// 可以判断错误是否为 nilif target == nil {return err == target}// 检查 error 是否为可比类型isComparable := reflectlite.TypeOf(target).Comparable()for {// 可比时,判定是否w诶目标,是则直接返回if isComparable && err == target {return true}// 判断是否实现 Is 接口,若实现了,则直接可以用// Is 接口判定,但是对于这里的 github.com/pkg/errors// 提供的几个结构都没有实现该接口if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {return true}// 若是没有实现 Is 接口,则调用 Unwrap,它会调用// 实际实现 error 接口的类型的 Unwrap 拿到底层错误// 实际取的就是 cause 存储的 error,Unrap 也是一个// 拆错误链的过程,边拆边比较看是否和目标相同if err = Unwrap(err); err == nil {return false}}}
文章转载自老码农空杯修行记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




