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 倉庫,正在更新中,你也可以從我往期的文章中看到一些說明 。

推薦閱讀