尽可能地避免在 for 循环中使用 defer,因为这可能会导致资源泄漏(Possible resource leak, 'defer' is called in the 'for' loop)。
defer 不是基于代码块的,而是基于函数的。你在循环中分配资源,那么不应该简单地使用 defer,因为释放资源不会尽可能早地发生(在每次迭代结束时),只有在 for 语句之后(所有迭代之后),即所在函数结束时,defer 函数才会被执行。这带来的后果就是,如果迭代次数过多,那么可能导致资源长时间得不到释放,造成泄漏。
// Bad
for rows.Next() {
fields, err := db.Query(.....)
if err != nil {
// ...
}
defer fields.Close()
// do something with `fields`
}
如果有一个类似上面分配资源的代码段,我们应该将其包裹在一个函数中(匿名函数或有名函数)。在该函数中,使用 defer,资源将在不需要时被立即释放。
// 1.将 defer 放在匿名函数中
for rows.Next() {
func() {
fields, err := db.Query(...)
if err != nil {
// Handle error and return
return
}
defer fields.Close()
// do something with `fields`
}()
}
// 2.将 defer 放在有名函数中然后调用之
func foo(r *db.Row) error {
fields, err := db.Query(...)
if err != nil {
return fmt.Errorf("db.Query error: %w", err)
}
defer fields.Close()
// do something with `fields`
return nil
}
// 调用有名函数
for rows.Next() {
if err := foo(rs); err != nil {
// Handle error and return
return
}
}