常用的error处理方式避免 if err != nil
What is Error
golang 中error本质上就是一个interface
type error interface {Error() string}
The error built-in interface type is the conventional interface for representing an error condition, with the nil value representing no error. error就是普普通通的接口,没有任何特殊的地方;接口为nil则代表没有错误
err1 := errors.New("错误信息1")err2 := fmt.Errorf("错误信息%d", 2)
Error Type
Sentinel Error
- eg: io.EOF
- 必须通过 == 判断
- 不够灵活,项目中对此会产生依赖
Error Types
- eg: os.PathError
- 使用断言获取自定义的更多的上下文
// package ostype PathError {Op stringPath stringErr error}
结论:需要通过类型switch判断,导致与调用者产生强耦合 Error Types 尽量避免使用
Opaque errors
- 不透明业务处理
- 只需要 err != nil 来2分处理
- 有时候需要对特殊的错误进行判断
- 定义私有接口
- 返回实现func 断言成功后返回 ```go type isErr interface { Timeout() bool }
- 定义私有接口
func IsTimeout(err error) bool { e, ok := err.(isErr) return ok && e.Timeout() }
> 结论:不需要了解err的底层结构,只对最终结果行为进行判断<a name="l47mR"></a># Handing Error- 正常逻辑不缩进代码,保持处理逻辑直线```go// 不推荐if err == nil {// do stuff}// deal err// 推荐:if err != nil {// deal err}// do stuff
- 不做处理的err,需要直接返回
```go // 不推荐 func Auth(r *Request) error { err := authenticate(r.User) if err != nil {
} return nil }return err
// 推荐 func Auth(r *Request) error { return authenticate(r.User) }
- io.Reader读取行数```go// 不推荐func CountLines(r io.Reader) (int, error) {var err errorsc := bufio.NewScanner(r)lines := 0for {_, err = br.ReadString('\n')lines++if err != nil {break}}if err != io.EOF {return 0, err}return lines, nil}// 推荐func CountLines(r io.Reader) (int, error) {sc := bufio.NewScanner(r)lines := 0for sc.Scan() {lines++}return lines, sc.Err()}
- write方法重写 ```go type Header struct { Key, Value string }
type Status struct { Code int Reason string }
// 不推荐 func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error { _, err := fmt.Fprintf(w, “HTTP/1.1 %d %s\r\n”, st.Code, st.Reason) if err != nil { return err }
for _, h := range headers {_, err := fmt.Fprintf(w, "%s: %s\r\n", h.Key, h.Value)if err != nil {return err}}if _, err := fmt.Fprintf(w, "\r\n"); err != nil {return err}_, err := io.Copy(w, body)return err
}
// 推荐 type errWrite struct { io.Writer err error }
func (e *errWrite) Write(buf []byte) (int, error) { if e.err != nil { return 0, e.err } var n int n, e.err = e.Writer.Write(buf) return n, nil }
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error { ew := &errWrite{Writer: w} fmt.Fprintf(ew, “HTTP/1.1 %d %s\r\n”, st.Code, st.Reason)
for _, h := range headers {fmt.Fprintf(ew, "%s: %s\r\n", h.Key, h.Value)}fmt.Fprintf(ew, "\r\n")io.Copy(ew, body)return ew.err
}
<a name="Nrpwi"></a>## Wrap errors- 现存的问题:- 直接返回error错误,不清楚堆栈信息,找不到那个代码触发文件触发的错误```gofunc Auth(r *Request) error {return authenticate(r.User)}
- 如果需要信息要自行拼接,
- 没有 file:line 信息
- 同时由于每一层调用都需要做类似处理,日志会散落各处
- 同时由于破坏了原有的err信息,不能再次进行等值判断
func Auth(r *Request) error {err := authenticate(r.User)if err != nil {return fmt.Errorf("Auth failed: %v", err)}return nil}
- 处理原则:
- 错误要被日志记录
- 应用处理错误,应该100%保证其完整性
- 之后不能在报告当前错误
- 解决实践:
github.com/pkg/errors
func ReadFile(path string) error {f, err := os.Open(path)if err != nil {return errors.Wrap(err, "open faild")}f.Close()return nil}func step1() error {return errors.WithMessage(ReadFile("xxxxxxx.json"), "step1 err")}func step2() error {return errors.WithMessage(step1(), "step2 err")}func main() {err := step2()if err != nil {// %+v 可以格式化打印堆栈信息fmt.Printf("%+v\n", err)fmt.Printf(errors.Cause(err).Error())var pErr *os.PathErrorif errors.As(err, &pErr) {fmt.Println(pErr.Path)}os.Exit(1)}fmt.Println("suc")os.Exit(1)}

Wrap使用原则
新增
%werr上面追加errorfmt.Errorf("someerr: %w", err)
Unwarp 解析追加的最底层
e2 := e1.Unwarp() e1的底层error是e2
Is
- 判断2个err是否相等
- As
- error类型映射,如果2个类型相符合,可以直接调用底层err的方法
1.13 版本不支持堆栈调用 所以不支持Wrap方法
- error类型映射,如果2个类型相符合,可以直接调用底层err的方法
