Отделите ожидаемую ошибку от паники
Недоступный файл или неверный пользовательский ввод часто относятся к ситуациям, которые функция должна представить обычным результатом ошибки. Паника меняет поток выполнения: текущая работа прекращается, затем выполняются отложенные вызовы при раскрутке стека в этой горутине. Выбор между ошибкой и паникой зависит от контракта функции и границ компонента. Если паника используется внутри реализации, внешняя граница должна ясно определять, что получит вызывающий код. Молчаливое подавление проблемы не делает вычисление успешным.
Проверьте место вызова recover
Восстановление требует, чтобы recover был вызван непосредственно внутри отложенной функции в той же горутине, где происходит паника. Просто добавить его в обычную вспомогательную функцию недостаточно. Отложенная функция может сама вызвать recover и затем передать полученное значение для обработки. Обработчик в родительской горутине не перехватывает панику дочерней горутины. Поэтому границы восстановления продумывают там, где действительно выполняется работа, а не только около места её запуска.
Разберите порядок на короткой истории
Пусть вызывающая функция отмечает начало, затем обращается к рабочей. Рабочая сначала регистрирует отложенную очистку, потом отложенный обработчик с recover, после чего вызывает panic. Отложенные вызовы выполняются в обратном порядке регистрации: сначала обработчик, затем очистка. Если восстановление прошло успешно и обработчик нормально завершился, рабочая функция заканчивается, а вызывающая продолжает работу после её вызова. Строка в рабочей функции, написанная непосредственно после panic, не выполняется. Такой порядок удобно проверять отдельными метками событий.
Не потеряйте смысл результата
После восстановления нужно определить, какие данные действительно готовы и что было завершено лишь частично. Уместность продолжения зависит от состояния системы. Например, перевод паники в возвращаемую ошибку на выбранной границе требует явного присваивания ошибки и аккуратного контракта результатов. Иначе вызывающий код может получить значения по умолчанию и принять их за успешное вычисление. Отложенная очистка помогает освободить ресурс, но сама по себе не откатывает уже выполненные внешние действия. Запись диагностики, возврат ошибки и восстановление целостности являются разными задачами.
Попробуйте на практике
Постройте трассировку вымышленной программы на бумаге, не меняя рабочий сервис.
- Запишите последовательность вызывающей функции: метка начала, вызов рабочей функции, метка продолжения. В рабочей укажите регистрацию очистки, регистрацию обработчика, панику и недостижимую после неё метку.
- Выпишите порядок меток при прямом вызове recover из отложенного обработчика. Предположите, что обработчик и очистка сами не вызывают новую панику.
- Поменяйте местами регистрацию очистки и обработчика. Снова проследите события и объясните, почему порядок отложенных действий изменился.
- Отдельно рассмотрите запуск рабочей функции в новой горутине, когда обработчик остался только у родителя. Укажите, почему прежнего восстановления уже нет.
- Опишите ожидаемый результат рабочей функции после восстановления: какие значения допустимы, где сообщается ошибка и какие частичные действия требуют отдельного разбора.
Как проверить результат. В первой трассировке обработчик предшествует очистке, вызывающая функция продолжает выполнение, а строка после panic внутри рабочей отсутствует. Граница горутины и смысл возвращаемой ошибки отмечены явно.
Частые вопросы
Можно ли одним recover защитить весь процесс от любой паники?
Нет. Восстановление связано с текущей горутиной и подходящим отложенным вызовом. Оно также не является универсальной защитой от всех видов аварийного завершения процесса.
Почему порядок defer важно проверять отдельно?
Зависимые действия могут требовать определённой последовательности. Если очистка и диагностика зарегистрированы без учёта обратного порядка, обработчик может увидеть уже изменённое состояние или недоступный ресурс.
Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.