panic, recover и defer
Когда вызывать panic и как корректно восстанавливаться
«panic, recover и defer» — бесплатный урок Go Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.
Что такое panic?
panic прекращает обычное выполнение текущей горутины, разворачивает стек вызовов, выполняя отложенные функции, и приводит к аварийному завершению программы, если панику не удалось перехватить:
package main
import "fmt"
func mustPositive(n int) int {
if n <= 0 {
panic(fmt.Sprintf("mustPositive: got %d", n))
}
return n
}
func main() {
fmt.Println(mustPositive(5)) // 5
// fmt.Println(mustPositive(-1)) // panics!
}Когда использовать panic
Используйте panic только в следующих случаях:
- Действительно неустранимые ошибки программиста (нарушенные инварианты)
- Невозможные условия, указывающие на ошибку в программе
- Сбои при запуске (отсутствующая конфигурация, неудачные подключения во время инициализации)
Никогда не используйте panic для ожидаемых условий отказа — вместо этого используйте error.
Основы defer
defer планирует вызов функции на момент возврата окружающей функции, независимо от способа возврата (обычный возврат, ошибка или паника):
package main
import "fmt"
func main() {
defer fmt.Println("third")
defer fmt.Println("second") // LIFO order
defer fmt.Println("first")
fmt.Println("running...")
// Output:
// running...
// first
// second
// third
}defer для очистки
Наиболее распространённое применение defer — обеспечение освобождения ресурсов:
package main
import ("fmt"; "os")
func readFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("open: %w", err)
}
defer f.Close() // always runs, even if we return early
buf := make([]byte, 1024)
n, _ := f.Read(buf)
return string(buf[:n]), nil
}recover — перехват паники
recover останавливает панику и возвращает её значение. Его необходимо вызывать непосредственно внутри функции defer:
package main
import "fmt"
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic: %v", r)
}
}()
return a / b, nil // panics if b==0
}
func main() {
r, err := safeDiv(10, 0)
fmt.Println(r, err)
// 0 recovered from panic: runtime error: integer divide by zero
}recover должен вызываться в отложенной функции
recover работает только при непосредственном вызове внутри отложенной функции. При вызове в другом месте он возвращает nil:
package main
import "fmt"
func wrong() {
// recover() here does nothing — not in defer
}
func correct() {
defer func() {
if r := recover(); r != nil {
fmt.Println("caught:", r)
}
}()
panic("oops")
}
func main() { correct() }panic в коде библиотеки
Код библиотеки почти никогда не должен позволять паникам доходить до вызывающего кода. Преобразуйте паники в ошибки на границе:
package main
import ("fmt"; "runtime/debug")
func safeCall(fn func()) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic: %v\n%s", r, debug.Stack())
}
}()
fn()
return nil
}
func main() {
err := safeCall(func() { panic("boom") })
fmt.Println(err != nil) // true
}Порядок defer по принципу LIFO и ловушка с циклами
Отложенные вызовы выполняются в порядке «последним добавлен — первым выполнен» (LIFO). Будьте осторожны, откладывая вызовы внутри циклов — используйте замыкание или вспомогательную функцию:
package main
import "fmt"
func processFiles(names []string) {
for _, name := range names {
name := name // capture loop variable
defer func() { fmt.Println("close:", name) }()
}
}
func main() {
processFiles([]string{"a.txt", "b.txt", "c.txt"})
// Output (LIFO): close: c.txt, close: b.txt, close: a.txt
}defer и именованные возвращаемые значения
Отложенные функции могут читать и изменять именованные возвращаемые значения — это полезно для добавления сведений об ошибках:
package main
import "fmt"
func doWork() (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("doWork: %w", err)
}
}()
return fmt.Errorf("something failed")
}
func main() {
err := doWork()
fmt.Println(err) // doWork: something failed
}panic и ошибка: руководство по выбору
Выбор между panic и ошибкой:
- Используйте ошибку: ожидаемые сбои (файл не найден, превышение времени ожидания сети, недопустимый ввод)
- Используйте panic: ошибки программирования (доступ к nil-карте, выход за границы), невозможные состояния, сбои инициализации
- Никогда не используйте panic через границы API — вызывающий код не сможет его обработать
- Перехватывайте паники на верхнем уровне горутины в серверах, чтобы избежать сбоев
Быстрая проверка
Где необходимо вызывать recover(), чтобы перехватить панику?
Повторение: panic, recover, defer
Итоги:
panic: раскручивает стек, выполняет отложенные вызовы и приводит к аварийному завершению, если не был перехваченdefer: выполняется при выходе из функции (обычном, из-за ошибки или паники); в порядке LIFOrecover: останавливает панику и возвращает значение; должен вызываться вdefer- Используйте панику для ошибок программиста, а не для ожидаемых сбоев
- Преобразуйте паники в ошибки на границах API
Изучай Go с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 51
- Уроки
- 203
Часто задаваемые вопросы
Урок «panic, recover и defer» бесплатный?
Да — полный текст урока «panic, recover и defer» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.
Чему я научусь в уроке «panic, recover и defer»?
Когда вызывать panic и как корректно восстанавливаться Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Go Academy?
Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «panic, recover и defer»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Go Academy?
Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Интерфейс error
- Создание пользовательских ошибок
- Обёртывание и распаковка ошибок
- panic, recover и defer