11 вопросов
Механизмы управления потоком выполнения: defer откладывает вызов до конца функции, panic вызывает аварийное завершение, recover перехватывает панику. Порядок выполнения defer - LIFO.
defer? 🟢 Лёгкий
▶
defer f() регистрирует вызов функции, который выполнится при выходе из текущей функции (return, panic). Часто используется для освобождения ресурсов: defer file.Close(), defer mu.Unlock().
Подробнее →Defer'ы складываются в стек (LIFO). Последний зарегистрированный выполняется первым. Это логично: ресурсы освобождаются в порядке, обратном получению.
Подробнее →recover()? 🟡 Средний
▶
recover() останавливает распространение паники и возвращает значение, переданное в panic(). Работает только внутри defer-функции. Если паники нет, возвращает nil.
Подробнее →recover()? 🟡 Средний
▶
recover() имеет эффект только внутри отложенной (deferred) функции. Вызов recover() в обычном коде всегда возвращает nil. Типичный паттерн: defer func() { if r := recover(); r != nil { log.Println(r) } }().
Подробнее →for _, f := range files { r, _ := os.Open(f); defer r.Close() }? 🟡 Средний
▶
defer привязан к функции, не к блоку цикла. Все файлы откроются, но закроются только при выходе из функции. При тысячах файлов - утечка файловых дескрипторов. Решение: вынести тело цикла в отдельную функцию или закрывать явно в конце итерации.
Подробнее →os.Exit(0)? 🟡 Средний
▶
os.Exit немедленно завершает процесс без выполнения deferred функций. Это отличает его от обычного return из main, при котором defer'ы выполняются. log.Fatal также вызывает os.Exit(1). Используйте os.Exit только когда defer'ы не нужны.
Подробнее →Аргументы defer f(x) вычисляются немедленно, а вызов откладывается. Частая ловушка: defer fmt.Println(i) зафиксирует текущее значение i, даже если оно потом изменится. Используйте замыкание для актуального значения.
Подробнее →Каждая горутина имеет свой стек вызовов. recover() работает только в той горутине, где произошла panic. Неперехваченная паника в горутине завершает всю программу.
Подробнее →func f() (r int) { defer func() { r++ }(); return 0 }? 🔴 Сложный
▶
return 0 устанавливает именованное возвращаемое значение r = 0. Затем выполняется defer, который увеличивает r до 1. Функция возвращает 1. Defer может модифицировать именованные возвращаемые значения после return.
Подробнее →panic("oops")? 🔴 Сложный
▶
recover() возвращает значение типа any, переданное в panic(). При panic("oops") вернётся строка "oops". При panic(42) вернётся int 42. Тип проверяется через type assertion. Без паники recover() вернёт nil.
Подробнее →func f() (err error) { if err := validate(); err != nil { return err }; return nil }? 🔴 Сложный
▶
Внутри if оператор := создает новую переменную err, затеняющую именованный возврат. Defer, проверяющий err, увидит nil. Решение: = вместо := или другое имя переменной.
Подробнее →