github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/docs/005-errors包源码分析.md (about) 1 ## errors 2 3 ```go 4 // The error built-in interface type is the conventional interface for 5 // representing an error condition, with the nil value representing no error. 6 type error interface { 7 Error() string 8 } 9 ``` 10 11 任何实现这个error interface 的类型/结构体都都可以赋值给error 12 13 14 第三方库: github.com/pkg/errors 15 16 ```go 17 // Wrap annotates cause with a message. 18 func Wrap(cause error, message string) error 19 // Cause unwraps an annotated error. 20 func Cause(err error) error 21 ``` 22 通过 Wrap 可以将一个错误,加上一个字符串,“包装”成一个新的错误;通过 Cause 则可以进行相反的操作,将里层的错误还原。 23 24 ```go 25 func ReadFile(path string) ([]byte, error) { 26 f, err := os.Open(path) 27 if err != nil { 28 return nil, errors.Wrap(err, "open failed") 29 } 30 defer f.Close() 31 32 buf, err := ioutil.ReadAll(f) 33 if err != nil { 34 return nil, errors.Wrap(err, "read failed") 35 } 36 return buf, nil 37 } 38 ``` 39 40 示例 一个错误可能被处理多次 41 42 ```go 43 func Write(w io.Writer, buf []byte) error { 44 _, err := w.Write(buf) 45 if err != nil { 46 // annotated error goes to log file 47 log.Println("unable to write:", err) 48 49 // unannotated error returned to caller return err 50 return err 51 } 52 return nil 53 } 54 ``` 55 优化方案 56 57 ```go 58 func Write(w io.Write, buf []byte) error { 59 _, err := w.Write(buf) 60 return errors.Wrap(err, "write failed") 61 } 62 ``` 63 64 在golang 1.13 开始,为了支持 wrapping,fmt.Errorf 增加了 %w 的格式,并且在 error 包增加了三个函数:errors.Unwrap,errors.Is,errors.As。 65 66 fmt.Errorf 67 68 使用 fmt.Errorf 加上 %w 格式符来生成一个嵌套的 error,它并没有像 pkg/errors 那样使用一个 Wrap 函数来嵌套 error,非常简洁。 69 70 Unwrap 71 72 func Unwrap(err error) error 73 74 将嵌套的 error 解析出来,多层嵌套需要调用 Unwrap 函数多次,才能获取最里层的 error。 75 76 ```go 77 func Unwrap(err error) error { 78 // 判断是否实现了 Unwrap 方法 79 u, ok := err.(interface { 80 Unwrap() error 81 }) 82 // 如果不是,返回 nil 83 if !ok { 84 return nil 85 } 86 // 调用 Unwrap 方法返回被嵌套的 error 87 return u.Unwrap() 88 } 89 ``` 90 对 err 进行断言,看它是否实现了 Unwrap 方法,如果是,调用它的 Unwrap 方法。否则,返回 nil。 91 92 Is 93 94 func Is(err, target error) bool 95 96 判断 err 是否和 target 是同一类型,或者 err 嵌套的 error 有没有和 target 是同一类型的,如果是,则返回 true。 97 98 ```go 99 func Is(err, target error) bool { 100 if target == nil { 101 return err == target 102 } 103 104 isComparable := reflectlite.TypeOf(target).Comparable() 105 106 // 无限循环,比较 err 以及嵌套的 error 107 for { 108 if isComparable && err == target { 109 return true 110 } 111 // 调用 error 的 Is 方法,这里可以自定义实现 112 if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { 113 return true 114 } 115 // 返回被嵌套的下一层的 error 116 if err = Unwrap(err); err == nil { 117 return false 118 } 119 } 120 } 121 ``` 122 123 通过一个无限循环,使用 Unwrap 不断地将 err 里层嵌套的 error 解开,再看被解开的 error 是否实现了 Is 方法,并且调用它的 Is 方法,当两者都返回 true 的时候,整个函数返回 true。 124 125 As 126 127 func As(err error, target interface{}) bool 128 129 从 err 错误链里找到和 target 相等的并且设置 target 所指向的变量。 130 131 ```go 132 func As(err error, target interface{}) bool { 133 // target 不能为 nil 134 if target == nil { 135 panic("errors: target cannot be nil") 136 } 137 138 val := reflectlite.ValueOf(target) 139 typ := val.Type() 140 141 // target 必须是一个非空指针 142 if typ.Kind() != reflectlite.Ptr || val.IsNil() { 143 panic("errors: target must be a non-nil pointer") 144 } 145 146 // 保证 target 是一个接口类型或者实现了 Error 接口 147 if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) { 148 panic("errors: *target must be interface or implement error") 149 } 150 targetType := typ.Elem() 151 for err != nil { 152 // 使用反射判断是否可被赋值,如果可以就赋值并且返回true 153 if reflectlite.TypeOf(err).AssignableTo(targetType) { 154 val.Elem().Set(reflectlite.ValueOf(err)) 155 return true 156 } 157 158 // 调用 error 自定义的 As 方法,实现自己的类型断言代码 159 if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { 160 return true 161 } 162 // 不断地 Unwrap,一层层的获取嵌套的 error 163 err = Unwrap(err) 164 } 165 return false 166 } 167 ``` 168 169 返回 true 的条件是错误链里的 err 能被赋值到 target 所指向的变量;或者 err 实现的 As(interface{}) bool 方法返回 true。 170 171 前者,会将 err 赋给 target 所指向的变量;后者,由 As 函数提供这个功能。 172 173 如果 target 不是一个指向“实现了 error 接口的类型或者其它接口类型”的非空的指针的时候,函数会 panic。 174