defer, panic и recover
defer откладывает вызов, panic роняет программу, recover её спасает. Тонкий, но важный механизм.
defer откладывает выполнение функции до момента, когда окружающая функция вернёт управление — что бы ни случилось.
defer: гарантированная уборка
defer ставит вызов «в очередь», и тот выполнится при выходе из функции — хоть через return, хоть через панику. Это идеальный способ закрывать ресурсы: файлы, соединения, мьютексы. Закрытие пишут сразу рядом с открытием, и забыть его невозможно.
package main
import "fmt"
func main() {
fmt.Println("начало")
defer fmt.Println("уборка (отложено)")
fmt.Println("работа")
}Вывод:
начало работа уборка (отложено)
Несколько defer выполняются в обратном порядке (LIFO, как стек). Типичный паттерн с файлом:
f, err := os.Open("data.txt")
if err != nil {
return err
}
defer f.Close() // закроется при любом выходе из функцииpanic: что-то пошло совсем не так
panic прерывает нормальное выполнение и начинает разматывать стек, попутно выполняя отложенные defer. Если панику не перехватить — программа аварийно завершится. Паникой пользуются редко: только для действительно невосстановимых ситуаций (баг в программе, невозможное состояние). Обычные ошибки — это error, а не паника.
recover: перехват паники
recover работает только внутри отложенной функции. Он останавливает разматывание стека и возвращает значение паники. Это нужно, например, чтобы один упавший обработчик не уронил весь сервер.
package main
import "fmt"
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("восстановлено после паники: %v", r)
}
}()
result = a / b // деление на 0 вызовет панику
return result, nil
}
func main() {
res, err := safeDivide(10, 0)
fmt.Println(res, err)
res, err = safeDivide(10, 2)
fmt.Println(res, err)
}Вывод:
0 восстановлено после паники: runtime error: integer divide by zero 5 <nil>
Здесь отложенная функция перехватывает панику от деления на ноль и превращает её в обычную ошибку через именованный результат err. Это редкий, но мощный приём.
| Механизм | Назначение |
defer | отложить вызов до выхода из функции (уборка) |
panic | аварийно прервать выполнение (редко) |
recover | перехватить панику внутри defer |
Итог
deferгарантирует уборку ресурсов при любом выходе; несколько — в обратном порядке.panic— только для невосстановимых ситуаций; обычные сбои — этоerror.recoverвнутриdeferперехватывает панику, например чтобы не ронять сервер.