Go 源码解读|如何用好 errors 库的 errors.Is 与 errors.As() 方法( 二 )


type TypicalErr struct {   e string}?func (t TypicalErr) Error() string {   return t.e}?func main() {   err := TypicalErr{"typical error"}   err1 := fmt.Errorf("wrap err: %w", err)   err2 := fmt.Errorf("wrap err1: %w", err1)   var e TypicalErr   if !errors.As(err2, &e) {      panic("TypicalErr is not on the chain of err2")   }   println("TypicalErr is on the chain of err2")   println(err == e)}/*打印结果:TypicalErr is on the chain of err2true*/来看一下 error.As 方法的源码:
func As(err error, target any) bool {   if target == nil {      panic("errors: target cannot be nil")   }   val := reflectlite.ValueOf(target)   typ := val.Type()   if typ.Kind() != reflectlite.Ptr || val.IsNil() {      panic("errors: target must be a non-nil pointer")   }   targetType := typ.Elem()   if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {      panic("errors: *target must be interface or implement error")   }   for err != nil {      if reflectlite.TypeOf(err).AssignableTo(targetType) {         val.Elem().Set(reflectlite.ValueOf(err))         return true    }      if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {         return true    }      err = Unwrap(err)   }   return false}源码 for 循环前的部分是用来约束 target 参数的类型,要求其是一个非空的指针类型 。
此外要求 *target 是一个接口或者实现了 error 接口 。
for 循环判断 err 是否可以赋值给 target 所属类型,如果可以则赋值返回 true 。
如果 err 实现了自己的 As 方法,则调用其逻辑 , 否则也是走递归拆包的逻辑 。
小结后续将继续分享一些源码解读的文章,关于 Go 语言的学习,我也开源了一个 GitHub 仓库,正在更新中,你也可以从我往期的文章中看到一些说明 。

推荐阅读