2023年2月22日
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, 将file的open与close封装在独立函数, 循环中不defer
- 方案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
}
- 问题: regardless of the execution path, notify and incrementCounter are always called with the same status: an empty string
- 方案1: 传引用
- 方案2: defer 闭包调用, 闭包在调用时才捕获值
func main() {
i := 0
j := 0
defer func(i int) {
fmt.Println(i, j)
}(i)
i++
j++
}
- 输出(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
}
- 问题: 忽略了rows.Close可能产生的问题, 通常应该由调用者决定怎么处理
- 解决1: defer 闭包,在闭包内,将错误记录日志
- 解决2: return err(编译不通过), 需要通过命名的返回参数返回err
- 问题: 已有的err与defer中的错误都要返回
- 方案1: 自定义error类型,融合多种错误类型
- 方案2: 返回一种错误类型,日志记录一种错误
defer func() {
closeErr := rows.Close()
if err != nil {
if closeErr != nil {
log.Printf("failed to close rows: %v", err)
}
return
}
err = closeErr
}()