20230222日志

20230222日志

2023年2月22日
2023
202302, go, defer

go #

defer: in a loop #

func readFiles(ch <-chan string) error {
  for path := range ch {
    file, err := os.Open(path)
    if err != nil {
      return err
    }
    defer file.Close()
    // Do something with file
  }
  return nil
}
  1. 方案1, 将file的open与close封装在独立函数, 循环中不defer
  2. 方案2, 循环内创建闭包调用, 在闭包中defer

defer: ignoring how defer arguments and receivers are evaluated #

func f() error {
  var status string
  defer notify(status)
  defer incrementCounter(status)
  if err := foo(); err != nil {
    status = StatusErrorFoo
    return err
  }
  if err := bar(); err != nil {
    status = StatusErrorBar
    return err
  }
  status = StatusSuccess
  return nil
}
  1. 问题: regardless of the execution path, notify and incrementCounter are always called with the same status: an empty string
  2. 方案1: 传引用
  3. 方案2: defer 闭包调用, 闭包在调用时才捕获值
func main() {
  i := 0
  j := 0
  defer func(i int) {
    fmt.Println(i, j)
  }(i)
  i++
  j++
}
  1. 输出(0, 1)

defer: not handling defer errors #

const query = "..."
func getBalance(db *sql.DB, clientID string) (
float32, error) {
  rows, err := db.Query(query, clientID)
  if err != nil {
  return 0, err
  }
  defer rows.Close()
  // Use rows
}
  1. 问题: 忽略了rows.Close可能产生的问题, 通常应该由调用者决定怎么处理
  2. 解决1: defer 闭包,在闭包内,将错误记录日志
  3. 解决2: return err(编译不通过), 需要通过命名的返回参数返回err
  4. 问题: 已有的err与defer中的错误都要返回
    1. 方案1: 自定义error类型,融合多种错误类型
    2. 方案2: 返回一种错误类型,日志记录一种错误
defer func() {
  closeErr := rows.Close()
  if err != nil {
    if closeErr != nil {
      log.Printf("failed to close rows: %v", err)
    }
    return
  }
  err = closeErr
}()