延迟函数的执行不仅被延迟,延迟到周围函数返回的那一刻,即使封闭函数突然终止,它也会被执行,例如恐慌。规格: 延迟声明: https://golang.org/ref/spec#Defer_statements
“defer”语句调用一个函数,该函数的执行被推迟到周围函数返回的那一刻,因为周围函数执行了返回声明 https://golang.org/ref/spec#Return_statements,到达其末端函数体 https://golang.org/ref/spec#Function_declarations, 或者因为对应的 goroutine 是惊慌失措 https://golang.org/ref/spec#Handling_panics.
每当您创建一个值或一个提供正确关闭/处置它的方法的资源时,您应该始终使用defer
声明以确保即使您的其他代码发生恐慌也能释放它,以防止泄漏内存或其他系统资源。
确实,如果您在循环中分配资源,则不应简单地使用defer
,因为这样就不会释放资源尽早 and should(在每次迭代结束时),仅在for
语句(仅在所有迭代之后)。
您应该做的是,如果您有一个分配此类资源的代码片段,请将其包装在一个函数中(匿名函数或命名函数),并且在该函数中您可以使用defer
,并且一旦不再需要资源就会被释放,重要的是即使你的代码中有一个可能会出现恐慌的错误。
Example:
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() https://golang.org/pkg/database/sql/#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
}