Go 语言入门指南:浅谈错误处理 | 青训营

88 阅读2分钟

错误处理

Go语言中,error 是一个接口

type error interface {
  Error() string
}

errors 是标准库的一个包,errorString 结构体是对 error接口的实现,我们也可以对 error 接口进行自定义实现

package errors
​
func New(text string) error {
  return &errorString{text}
}
​
// errorString is a trivial implementation of error.
type errorString struct {
  s string
}
​
func (e *errorString) Error() string {
  return e.s
}

我们可以使用 errors.New 来表示一个最基本的错误,

// 返回 error接口(引用类型)
var err = errors.New("404 Not Found")

错误嵌套

  • Wrap 一个错误链

    func test() error {
      // 有格式化需求时使用 fmt.Errorf 和 %w 来将一个错误 wrap 至错误链中
      err0 := err
      err1 := fmt.Errorf("第一层格式化输出 %w", err0)
      err2 := fmt.Errorf("第二层格式化输出 %w", err1)
      return err2
    }
    
  • Unwrap 一个错误链

    err1 := errors.Unwrap(err2)
    err0 := errors.Unwrap(err1)
    
  • 使用 errors.Is 判定错误链上的所有错误是否包含有特定的错误(特定是指地址相同的同一个实例)
  • 使用 errors.As 在错误链上获取指定类型的错误

panic函数用于指示无法修复的错误,直接中断程序执行

recover函数用于捕获panic,避免程序崩溃异常退出

完整代码如下:

// 返回 error接口(引用类型)
var err = errors.New("404 Not Found")
​
func test() error {
  // 有格式化需求时使用 fmt.Errorf 和 %w 来将一个错误 wrap 至错误链中
  err0 := err
  err1 := fmt.Errorf("第一层格式化输出 %w", err0)
  err2 := fmt.Errorf("第二层格式化输出 %w", err1)
  return err2
}
​
func main() {
  err2 := test()
  if err2 != nil { // Unwrap 一个错误链
    fmt.Println(err2)
    err1 := errors.Unwrap(err2)
    fmt.Println(err1)
    err0 := errors.Unwrap(err1)
    fmt.Println(err0)
    // 判定错误链上的所有错误是否包含有特定的错误,特定是指同一个实例(地址相同),从源码可以看出是对接口使用==进行比较的
    if errors.Is(err2, err) {
      // 在错误链上获取指定类型的错误
      var getErr error
      if errors.As(err2, &getErr) {
        fmt.Println(getErr)
      }
    }
  }
  defer func() {
    // recover 函数用于捕获并处理 panic,而不是直接崩溃异常退出
    // recover 只能在被 defer 的函数中使用,并且只有在发生panic时、在当前协程生效
    // 在其他情况下调用recover函数将不会产生任何效果。
    rc := recover()
    switch rc.(type) {
    case runtime.Error: // 运行时错误
      fmt.Println("runtime error:", rc)
    default: // 非运行时错误
      fmt.Println("recover:", rc)
    }
  }()
  // panic 用于出现异常时中断程序,直接执行 defer 后的代码
  panic("无法修复的错误")
}