Как требование UnwindSafe ограничивает данные, захватываемые замыканием для catch_unwind?
catch_unwind принимает замыкание только при наличии у него свойства UnwindSafe. Это означает, что захваченные данные должны считаться пригодными для пересечения границы паники: после незавершённого изменения их состояние не должно приводить к нарушению логических инвариантов.
UnwindSafe не гарантирует отсутствие паники и не является механизмом защиты памяти. Это маркерная проверка, которая заставляет явно подтвердить безопасность в сомнительных случаях через AssertUnwindSafe.
При панике с режимом раскрутки стека Rust уничтожает локальные значения и передаёт управление наружу. В отличие от обычного возврата ошибки, операция может прерваться между двумя изменениями состояния объекта.
Поэтому Rust отделяет обычную обработку Result от перехвата паники. Требование UnwindSafe решает проблему пересечения границы раскрутки стека с данными, которые могли остаться в логически противоречивом состоянии.
Замыкание может изменить внешнее состояние, а затем вызвать панику. Если паника будет перехвачена, программа продолжит выполнение, но изменённые данные уже могут не соответствовать инвариантам структуры.
Особенно опасны захваченные изменяемые ссылки и типы с внутренней изменяемостью. Компилятор не доказывает корректность бизнес-инвариантов, поэтому UnwindSafe предоставляет консервативную статическую границу, а не полную проверку корректности.
Сигнатура catch_unwind требует, чтобы замыкание реализовывало UnwindSafe. Автоматическая реализация этого маркерного трейта распространяется на многие типы, если их составные части считаются безопасными для пересечения границы unwind.
Тип может не удовлетворять UnwindSafe, когда паника способна оставить его в промежуточном состоянии, которое нельзя безопасно наблюдать после перехвата. &mut T и некоторые типы с внутренней изменяемостью являются типичными примерами ситуаций, для которых компилятор не делает безусловного предположения о безопасности.
AssertUnwindSafe оборачивает замыкание и сообщает компилятору, что разработчик сам проверил инварианты. Это не откатывает изменения, не восстанавливает состояние и не делает небезопасный код безопасным автоматически.
Такое утверждение оправдано, если после перехвата состояние не используется, изменения атомарны с точки зрения инварианта или замыкание работает с изолированной копией. Если обработчик продолжает использовать частично изменённый объект, лучше изменить дизайн: применить временное состояние, откат, возврат Result либо изолировать операцию в отдельном процессе.
Ограничение действует именно для раскручиваемых паник. При профиле с panic = abort процесс завершается без раскрутки, поэтому catch_unwind не может продолжить выполнение после такой паники.
Библиотека запускает пользовательский callback, который получает доступ к внутреннему состоянию сервиса. Callback может вызвать панику, но библиотека хочет продолжить работу и записать диагностическую информацию.
Первый вариант — передать callback прямую изменяемую ссылку и обернуть вызов в AssertUnwindSafe. Плюс — простая реализация; минус — после паники внутреннее состояние может быть частично изменено, а ручное утверждение скрывает этот риск от компилятора.
Второй вариант — выполнить callback над отдельной копией состояния, а применить изменения только после успешного завершения. Это требует клонирования или транзакционной модели, зато после перехвата паники основное состояние остаётся согласованным.
Третий вариант — не перехватывать панику внутри библиотеки и требовать от вызывающего кода использовать Result. Это проще для дизайна ошибок, но не защищает библиотеку от паники пользовательского кода.
Рациональный выбор — изолированная копия или транзакционный буфер, если продолжение работы действительно необходимо. AssertUnwindSafe следует применять только после явного доказательства, что частичное изменение не нарушает инварианты; одним фактом перехвата паники такое решение не обосновывается.
UnwindSafe, что после перехвата паники состояние объекта автоматически восстановлено?Нет. Это только маркер допустимости пересечения границы unwind. Значения уже выполненных присваиваний, побочные эффекты ввода-вывода и изменения внешних ресурсов не откатываются. Восстановление состояния должно быть частью архитектуры операции.
AssertUnwindSafe?Технически это часто устраняет требование трейта, но семантически решение корректно только после проверки инвариантов. Обёртка переносит ответственность с компилятора на разработчика и особенно опасна при совместном использовании частично изменённых данных после паники.
UnwindSafe отличается от безопасности памяти?UnwindSafe относится к логической согласованности состояния при раскрутке стека. Он не заменяет правила владения, заимствования и синхронизации и не разрешает небезопасный доступ к памяти. Код может быть memory-safe, но не быть логически безопасным для продолжения работы после перехваченной паники.