← Назад к вопросам

Что выведет код? defer и panic

1.0 Junior🔥 231 комментариев
#Основы Go

Условие

Определите, что выведет следующий код:

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")
}

Вопросы

  1. Что выведет программа?
  2. В каком порядке выполнятся defer?
  3. Какие defer не выполнятся и почему?

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение: Что выведет код с 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):

  1. Выполняются все defer текущей функции в обратном порядке
  2. Паника распространяется вверх в вызывающую функцию
  3. Процесс повторяется

В анонимной функции:

  • 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

  1. defer регистрируется при выполнении оператора defer, не раньше
  2. Defer выполняются в LIFO порядке (последний в, первый из)
  3. При panic раскручивается стек, выполняя defer
  4. После panic выполняются все зарегистрированные defer вверх по стеку
  5. Если panic не поймана программа завершается с паникой
  6. 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.

Что выведет код? defer и panic | PrepBro