Panic, Defer и Recover
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 57 из 107.
Хотя Go делает акцент на возврате errors, некоторые ситуации действительно невозможно исправить. Для таких случаев Go предоставляет panic, defer и recover — механизм обработки исключительных обстоятельств.
panic немедленно останавливает обычное выполнение. Он предназначен для программных ошибок, например при обращении к индексу за пределами допустимого диапазона, а не для ожидаемых ошибок, таких как некорректный пользовательский ввод:
func MustGetConfig(key string) string {
value, exists := config[key]
if !exists {
panic("missing required config: " + key)
}
return value
}defer планирует выполнение функции при возврате окружающей функции — независимо от того, происходит ли это обычным образом или из-за panic. Отложенные вызовы execute в обратном порядке (последним добавлен — первым выполнен):
func ProcessFile() {
fmt.Println("Opening file")
defer fmt.Println("Closing file")
fmt.Println("Processing...")
// Вывод: Opening file, Processing..., Closing file
}recover перехватывает панику и возвращает обычное выполнение. Он работает только внутри deferred-функции:
func SafeOperation() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from panic: %v", r)
}
}()
riskyOperation()
return nil
}Этот шаблон converts panic в обычный error, позволяя вашей программе продолжить работу. Используйте его sparingly: предпочитайте возвращать errors для ожидаемых случаев сбоя, а panic/recover оставляйте для действительно исключительных ситуаций, таких как повреждённое состояние или неисправимые условия.
Задание
ЛегкоДавайте создадим безопасный калькулятор деления, который демонстрирует, как использовать panic, defer и recover, чтобы корректно обрабатывать исключительные ситуации. Калькулятор будет выполнять потенциально опасные операции и преобразовывать panic в обычные error, которые можно обрабатывать стандартным образом.
Вы организуете код в двух файлах:
calculator.go: создайте логику деления с восстановлением после panic.Реализуйте
Dividefunction, которая принимает два целых числа и вызывает panic с сообщением"division by zero", если divisor равен нулю. В противном случае она возвращает целочисленный результат деления.Реализуйте
SafeDividefunction, которая принимает два целых числа и возвращает целочисленный результат и error. Эта function должна:- Использовать
deferс anonymous function для восстановления после любого panic - Если panic был recovered, преобразовать его в error с форматом
"calculation error: [panic message]" - Внутри вызвать function
Divide - Вернуть результат и
nilпри успешном выполнении или ноль и error, если panic был recovered
- Использовать
main.go: считайте два целых числа из input и используйтеSafeDivide, чтобы безопасно выполнить calculation. Также продемонстрируйтеdefer, выводя сообщения об очистке в правильном order.Ваша main function должна:
- Вывести
Starting calculationв начале - Использовать
defer, чтобы запланировать выводCleanup complete - Использовать ещё один
defer, чтобы запланировать выводReleasing resources - Вызвать
SafeDivideс input values - Вывести либо
Result: [value], либоError: [error message]в зависимости от результата
- Вывести
Будут предоставлены следующие input:
- Строка 1: First целое число (dividend)
- Строка 2: Второе целое число (divisor)
Помните, что deferred calls выполняются в reverse order (last-in, first-out), поэтому сообщения об очистке должны появиться в порядке, противоположном порядку их deferred.
Например, для 20 и 4 ваш Output должен быть таким:
Starting calculation
Result: 5
Releasing resources
Cleanup completeА для 10 и 0 ваш Output должен быть таким:
Starting calculation
Error: calculation error: division by zero
Releasing resources
Cleanup completeПопробуйте сами
package main
import (
"fmt"
)
func main() {
// Прочитать ввод
var dividend, divisor int
fmt.Scanln(÷nd)
fmt.Scanln(&divisor)
// Вывести начальное сообщение
fmt.Println("Starting calculation")
// TODO: Use defer to schedule "Cleanup complete" message
// TODO: Use defer to schedule "Releasing resources" message
// Помните: отложенные вызовы выполняются в порядке LIFO (last-in, first-out)
// TODO: Вызвать SafeDivide с входными значениями
// TODO: Вывести либо "Result: [value]", либо "Error: [error message]"
// в зависимости от того, была ли возвращена ошибка
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker PoolПотренируйтесь самостоятельно: Онлайн-компилятор Go