Что выведет код? defer и panic
Условие
Определите, что выведет следующий код:
package main
import "fmt"
func main() {
defer fmt.Println("A")
defer fmt.Println("B")
func() {
defer fmt.Println("C")
panic("panic!")
defer fmt.Println("D")
}()
defer fmt.Println("E")
fmt.Println("F")
}
Вопросы
- Что выведет программа?
- В каком порядке выполнятся defer?
- Какие defer не выполнятся и почему?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение: Что выведет код с defer и panic
Ответ на вопрос 1
Программа выведет:
C
B
A
Почему F не выводится?
Panic прерывает выполнение кода в текущей функции. Строка fmt.Println("F") и defer fmt.Println("E") никогда не выполнятся, потому что они находятся после вызова анонимной функции, которая паникует.
Пошаговое выполнение
Шаг 1: Регистрация defer в main
defer fmt.Println("A") // defer stack: [A]
defer fmt.Println("B") // defer stack: [B, A]
Deferы добавляются в стек LIFO (Last In, First Out).
Шаг 2: Вызов анонимной функции
func() {
defer fmt.Println("C") // defer stack (локальный): [C]
panic("panic!") // ПАНИКА!
defer fmt.Println("D") // Эта строка НЕ выполнится
}()
Шаг 3: Обработка паники
Паника вызывает раскрутку стека (unwinding):
- Выполняются все defer текущей функции в обратном порядке
- Паника распространяется вверх в вызывающую функцию
- Процесс повторяется
В анонимной функции:
defer fmt.Println("C")выполняется → выводит "C"defer fmt.Println("D")НЕ выполняется (была после panic)
В main:
- Паника распространяется из анонимной функции
fmt.Println("F")НЕ выполняется (находится после вызова функции)defer fmt.Println("E")НЕ выполняется (была зарегистрирована, но panic произошёл ДО этой строки)- Выполняются defer в порядке LIFO: B, потом A
Результат выполнения:
C (defer из анонимной функции)
B (второй defer из main)
A (первый defer из main)
Ключевой момент: Когда регистрируется defer
defer fmt.Println("E") // Эта строка не выполняется, поэтому defer не регистрируется!
Второй важный момент: defer регистрируется при выполнении операции defer, а не при определении. Если panic происходит до этой строки, defer не регистрируется.
Порядок выполнения defer (LIFO)
Порядок регистрации в main:
1. defer A
2. defer B
← panic возникает ВНЕ main
Порядок выполнения:
2. B (последний зарегистрированный)
1. A (первый зарегистрированный)
Дополнительный пример для ясности
func example() {
defer fmt.Println("1") // регистрируется
defer fmt.Println("2") // регистрируется
panic("error") // паника
defer fmt.Println("3") // НЕ регистрируется
fmt.Println("4") // НЕ выполняется
}
// Вывод:
// 2
// 1
// panic: error
Более сложный пример с recover
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Caught:", r)
}
}()
defer fmt.Println("A")
defer fmt.Println("B")
func() {
defer fmt.Println("C")
panic("panic!")
defer fmt.Println("D")
}()
defer fmt.Println("E")
fmt.Println("F")
}
// Вывод:
// C
// B
// A
// Caught: panic!
Здесь recover() ловит панику, поэтому A и B выполняются, потом выполняется recover() функция.
Ответы на вопросы
1. Что выведет программа?
C
B
A
2. В каком порядке выполнятся defer?
- Сначала C (из анонимной функции, в обратном порядке регистрации)
- Потом B (из main, второй зарегистрированный)
- Потом A (из main, первый зарегистрированный)
3. Какие defer не выполнятся и почему?
- D — зарегистрирован после panic, поэтому не выполняется
- E — не зарегистрирован вообще (строка кода не достигнута из-за panic)
Правила defer и panic
- defer регистрируется при выполнении оператора
defer, не раньше - Defer выполняются в LIFO порядке (последний в, первый из)
- При panic раскручивается стек, выполняя defer
- После panic выполняются все зарегистрированные defer вверх по стеку
- Если panic не поймана программа завершается с паникой
- recover() может поймать панику в defer
Визуализация стека
Время →
[main]
├─ defer A
├─ defer B
├─ [anon function call]
│ ├─ defer C
│ └─ PANIC!
│ └─ выполняем C (defer из anon)
│ └─ выполняем B (defer из main, LIFO)
│ └─ выполняем A (defer из main, LIFO)
├─ [E - НЕ ЗАРЕГИСТРИРОВАНА]
└─ [F - НЕ ВЫПОЛНЕНА]
Это критическая концепция для понимания обработки ошибок в Go.