Модульні уроки (5/5)
`defer`: гарантоване очищення
defer відкладає виконання виклику до функції включення
повертає. Він працює, навіть якщо функція виходить через паніку. Кілька defer
у тій самій функції, що виконується в порядку LIFO (останній прийшов, перший вийшов).
Це ідіоматичний механізм для очищення ресурсу: закриття файлів, розблокування м'ютексів, звільнення підключень, відновлення стану.
Синтаксис і канонічний варіант використання
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // chiamata SEMPRE all'uscita della funzione
// ... usa f liberamente ...
return nilТиповий: f.Close(), mu.Unlock(), db.Close(), tx.Rollback(),
recover().
Аргументи розглядаються негайно, виконання відкладено
Коли ви пишете defer foo(x), x оцінюється під час
defer, не під час виконання:
x := 10
defer fmt.Println(x) // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"Замовлення ЛІФО
Відкладає стек і виконання, починаючи з останнього:
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1Це узгоджується із семантикою ресурсу: останній відкритий є першим бути закритим.
defer + recover: боротьба з панікою
defer — єдиний спосіб перехопити panic:
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recuperato:", r)
}
}()
panic("boom")
}Підводний камінь: defer в циклі
Розміщення defer f.Close() у циклі накопичує відкладення до кінця
функція, а не кожної ітерації. У довгому циклі ви відкриваєте багато файлів
і закривайте їх лише після завершення роботи функції: ризик витоку дескриптора файлу.
// MALE: defer accumulati
for _, path := range paths {
f, _ := os.Open(path)
defer f.Close() // chiude TUTTI alla fine della funzione esterna
// ...
}
// MEGLIO: estrai in una funzione
for _, path := range paths {
elabora(path) // ogni chiamata ha il proprio scope di defer
}Спробуйте
Надрукуйте «inizio», потім відкладіть друк «fine» за допомогою «defer», потім надрукуйте «mezzo». Результат має бути: inizio / mezzo / fine.
Показати підказку
Відкладення запускається, коли main() збирається повернутися.
Рішення доступне після 3 спроб
Напишіть три послідовні відкладення, які друкують 1, 2 і 3 у такому порядку в коді. Вихід буде 3 / 2 / 1 (LIFO).
Показати підказку
Три відкладення послідовно; пам'ятайте, що вони виконуються у зворотному порядку.
Рішення доступне після 3 спроб
Що це друкує? (примітка: аргументи відкладення оцінюються негайно)
x := 10
defer fmt.Println(x)
x = 99Підведення підсумків
defer call()відкладає виконання, доки функція не повернеться.- Кілька відкладень = замовлення LIFO.
- Аргументи оцінено негайно, виконання відкладено.
- Використовується для детермінованого очищення: файлів, м'ютексів, з'єднань.
defer+recover()перехоплюєpanic(рідко, на кордонах).- Ніколи не
deferвсередині довгого циклу: він накопичується до повернення зовнішньої функції.