Выполнение отложенной функции не только откладывается, откладывается до момента возврата окружающей функции, но также выполняется, даже если объемлющая функция внезапно завершается, например. паника. Спецификация: операторы отсрочки:
Оператор defer вызывает функцию, выполнение которой отложено до момента возврата из окружающей функции, либо потому, что окружающая функция выполнила оператор return достиг конца своего тела функции, или потому что соответствующая горутина паникует.
Всякий раз, когда вы создаете значение или ресурс, который предоставляет средства для его правильного закрытия / удаления, вы всегда должны использовать оператор defer
, чтобы убедиться, что он освобожден, даже если ваш другой код паникует, чтобы предотвратить утечку памяти или других системных ресурсов.
Это правда, что если вы выделяете ресурсы в цикле, вы не должны просто использовать defer
, так как тогда высвобождение ресурсов не произойдет так рано, как могло бы и должно (в конец каждой итерации), только после оператора for
(только после всех итераций).
Что вам нужно сделать, так это то, что если у вас есть фрагмент, который выделяет такие ресурсы, оберните его в функцию — анонимную или именованную — и в этой функции вы можете использовать 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
}
person
icza
schedule
10.08.2017