资源管理与错误处理

Go学习笔记

Defer

调用特点

  • 确保调用在函数结束时发生

  • defer列表为后进先出,参数在defer语句时才计算

    func tryDefer() {
      defer fmt.Println(1)
      defer fmt.Println(2)
      fmt.Println(3)
      //return
      panic("error occurred")
      fmt.Println(4)
    }
    ---------
    3
    2
    1
    

常见使用defer调用场景

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter

错误处理

  • 常用错误处理

    file, err := os.OpenFile(filename,
          os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
      if pathError, ok := err.(*os.PathError); !ok {
        panic(err)
      } else {
        fmt.Printf("%s, %s, %s\n",
            pathError.Op,
            pathError.Path,
            pathError.Err)
      }
      return
    }
    defer file.Close()
    
  • 服务器统一错误处理

    type appHandler func(writer http.ResponseWriter,
          request *http.Request) error
      
    func errWrapper(
          handler appHandler) func(
          http.ResponseWriter, *http.Request) {
      return func(writer http.ResponseWriter,
              request *http.Request) {
          // panic
          defer func() {
              if r := recover(); r != nil {
                  log.Printf("Panic: %v", r)
                  http.Error(writer,
                      http.StatusText(http.StatusInternalServerError),
                      http.StatusInternalServerError)
              }
          }()
      
          err := handler(writer, request)
      
          if err != nil {
              log.Printf("Error occurred "+
                      "handling request: %s",
                  err.Error())
      
              // user error
              if userErr, ok := err.(userError); ok {
                  http.Error(writer,
                      userErr.Message(),
                      http.StatusBadRequest)
                  return
              }
      
              // system error
              code := http.StatusOK
              switch {
              case os.IsNotExist(err):
                  code = http.StatusNotFound
              case os.IsPermission(err):
                  code = http.StatusForbidden
              default:
                  code = http.StatusInternalServerError
              }
              http.Error(writer,
                  http.StatusText(code), code)
          }
      }
    }
    type userError interface {
      error
      Message() string
    }
    

Panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的defer
  • 如果没有遇见recover,程序退出

Recover

  • 仅在defer调用中使用

  • 获取panic的值

  • 如果无法处理,可以重新panic

    func tryRecover() {
      defer func() {
          r := recover()
          if r == nil {
              fmt.Println("Nothing to recover. " +
                  "Please try uncomment errors " +
                  "below.")
              return
          }
          if err, ok := r.(error); ok {
              fmt.Println("Error occurred:", err)
          } else {
              panic(fmt.Sprintf(
                  "I don't know what to do: %v", r))
          }
      }()
      
      // Uncomment each block to see different panic
      // scenarios.
      // Normal error
      //panic(errors.New("this is an error"))
      
      // Division by zero
      //b := 0
      //a := 5 / b
      //fmt.Println(a)
      
      // Causes re-panic
      //panic(123)
    }
    

Error vs Panic

  • 意料之中的使用error,如:文件打不开等
  • 意料之外的使用panic,如:数组越界
Go 

See also