При возникновении второй panic во время раскрутки стека почему Rust аварийно завершает процесс вместо передачи ошибки вызывающему коду?
Если во время раскрутки стека, вызванной первой panic, возникает вторая panic, Rust аварийно завершает процесс. Передать обе паники вызывающему коду нельзя безопасно: первая уже находится в процессе обработки, а вторая возникла во время уничтожения объектов и может нарушить сам механизм очистки.
Модель panic с раскруткой стека предназначена для контролируемого аварийного завершения текущей ветки выполнения с запуском деструкторов уже созданных значений. Это позволяет освободить ресурсы и корректно выполнить код очистки даже при непредвиденной ошибке.
Однако раскрутка сама использует сложный внутренний механизм поиска обработчика и вызова Drop. Повторная паника в этот момент создаёт конфликт между двумя незавершёнными аварийными путями, поэтому Rust выбрал немедленное завершение процесса как безопасное и однозначное поведение.
Рассмотрим тип, чей метод Drop::drop может вызвать panic. Если такой объект уничтожается в обычном режиме, паника из drop начинает обычную раскрутку стека. Но если объект уничтожается уже из-за другой паники, drop запускается внутри существующей раскрутки.
Второй сбой означает, что очистка сама больше не является надёжной. Продолжение раскрутки могло бы привести к частично уничтоженному состоянию, потере исходной причины или повторному вызову небезопасной очистки. Поэтому результатом становится аборт процесса, а не обычный Result и не перехватываемая внешним кодом ошибка.
При первой панике Rust начинает раскрутку стека и вызывает Drop для локальных значений в соответствии с правилами времени жизни. Если любой из этих деструкторов вызывает вторую панику, Rust обнаруживает панику во время уже активной раскрутки и завершает процесс.
В примере первая паника начинает уничтожение _value. Его drop вызывает вторую панику, после чего процесс аварийно завершается. Внешний catch_unwind не превращает такую ситуацию в обычный безопасно обрабатываемый результат: двойная паника во время раскрутки приводит к аборту.
Это отличается от настройки panic = "abort": при ней уже первая паника завершает процесс без раскрутки стека. При стратегии раскрутки обычную единственную панику можно перехватить, но вторую панику внутри текущей раскрутки — нет.
Практический вывод: Drop не должен паниковать, особенно в типах библиотечного уровня. Ошибки освобождения ресурсов лучше сообщать заранее, возвращать из явного метода или сохранять в состоянии объекта, если это действительно необходимо.
Библиотека управляет временным файлом: объект-охранник удаляет файл в Drop, а при ошибке удаления вызывает panic. Если пользовательский код уже завершился паникой, уничтожение охранника запускает вторую панику и процесс аварийно завершается, теряя исходный контекст.
Возможны три подхода:
Drop. Это предотвращает вторую панику, но может оставить ресурс и скрыть проблему.Result, а в Drop использовать только безопасную best-effort-очистку с диагностическим журналированием.Для библиотечного API обычно выбирают третий вариант. Основная операция получает возможность обработать ошибку явно, а Drop остаётся неопасным даже при уже активной панике. В результате сохраняется исходная причина сбоя и снижается риск принудительного завершения процесса.
panic! в Drop приводит к немедленному завершению процесса?Нет. Если Drop вызывается без активной предыдущей паники, его паника может начать обычную раскрутку стека и, при наличии подходящего обработчика, быть перехвачена через catch_unwind. Немедленный аборт является следствием именно второй паники во время уже выполняющейся раскрутки либо использования стратегии panic = "abort".
Надёжно сделать это в самом Drop, вызванном во время раскрутки, нельзя: вызов panic! там создаст двойную панику. Безопасный дизайн не вызывает панику из Drop; ошибку очистки можно сохранить в заранее предусмотренном состоянии, отправить в диагностический канал без паники или вернуть из явного метода, который вызывается до уничтожения объекта.
Result предпочтительнее паники для операций, которые могут не удаться при освобождении ресурса?Result позволяет вызывающему коду выбрать политику: повторить операцию, записать диагностику, продолжить работу или завершить процесс. Drop не может вернуть Result, поэтому ошибка, превращённая там в панику, теряет структурированный интерфейс обработки и при активной панике может привести к немедленному аборту. Разделение явного завершения операции и автоматической очистки делает поведение предсказуемее.