“延迟”语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻,或者因为周围函数执行了return statement,到达了它的function body的末尾,或者因为对应的goroutine是panicking.
每当您创建一个提供正确关闭/处置它的方法的值或资源时,您应该始终使用 defer 语句来确保它被释放,即使您的其他代码发生恐慌以防止内存或其他系统资源泄漏。
确实,如果你在循环中分配资源,你不应该简单地使用defer,因为那样就不会释放资源尽早和应该(在每次迭代结束时),仅在 for 语句之后(仅在所有迭代之后)。
你应该做的是,如果你有一个分配此类资源的 sn-p,将其包装在一个函数中——匿名函数或命名函数——,在该函数中你可以使用defer,资源将被释放为一旦不再需要它们,重要的是即使您的代码中存在可能会恐慌的错误。
例子:
for rows.Next() {
func() {
fields, err := db.Query(...)
if err != nil {
// Handle error and return
return
}
defer fields.Close()
// do something with `fields`
}()
}
或者如果放入命名函数:
func foo(rs *db.Rows) {
fields, err := db.Query(...)
if err != nil {
// Handle error and return
return
}
defer fields.Close()
// do something with `fields`
}
并称它为:
for rows.Next() {
foo(rs)
}
此外,如果您想在第一个错误时终止,您可以从 foo() 返回错误:
func foo(rs *db.Rows) 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
}
}
还要注意 Rows.Close() 返回一个错误,当使用 defer 调用时该错误被丢弃。如果我们想检查返回的错误,我们可以使用这样的匿名函数:
func foo(rs *db.Rows) (err error) {
fields, err := db.Query(...)
if err != nil {
return fmt.Errorf("db.Query error: %w", err)
}
defer func() {
if err = fields.Close(); err != nil {
err = fmt.Errorf("Rows.Close() error: %w", err)
}
}()
// do something with `fields`
return nil
}