panic key что это
Как работает panic в Rust
Мне не удалось найти документы, объясняющие общую картину паники в Rust, так что это стоит записать.
(Бесстыдная статья: причина, по которой я заинтересовался этой темой, заключается в том что @Aaron1011 реализовал поддержку раскручивания стека в Miri.
Я хотел увидеть это в Miri с незапамятных времён, и у меня никогда не было времени, чтобы реализовать это самому, поэтому было действительно здорово видеть, как кто-то просто отправляет PR для поддержки этого на ровном месте.
После большого количества раундов проверки кода, он был влит совсем недавно.
Всё ещё есть некоторые грубые края, но основы определены точно.)
Целью этой статьи является документирование структуры высокого уровня и соответствующих интерфейсов, которые вступают в игру на стороне Rust.
Фактический механизм разматывания стека — это совершенно другой вопрос (о котором я не уполномочен говорить).
Примечание: эта статья описывает панику с этого коммита.
Многие из описанных здесь интерфейсов являются нестабильными внутренними деталями libstd и могут измениться в любое время.
Высокоуровневая структура
Что ещё хуже, RFC, описывающий ловушки паники, называет их «обработчик паники», но этот термин с тех пор был переопределён.
Я думаю, что лучшее место для начала — это интерфейсы, управляющие двумя направлениями:
Обработчик паники используется libcore для реализации (а) паники, вставляемой генерацией кода (такой как паника, вызванная арифметическим переполнением или индексацией массива/среза за пределами границ) и (b) core::panic! макрос (это макрос panic! в самой libcore и в #[no_std] контексте #[no_std] ).
Оба эти интерфейса реализуются через extern блоки: listd/libcore, соответственно, просто импортируют некоторую функцию, которой они делегируют, и где-то совсем в другом месте в дереве крейтов эта функция реализуется.
Импорт разрешается только во время связывания; Глядя локально на код нельзя сказать, где живёт фактическая реализация соответствующего интерфейса.
Неудивительно, что по пути я несколько раз терялся.
В дальнейшем оба этих интерфейса будут очень полезны; когда вы запутались. Первое, что нужно проверить, это не перепутали ли вы обработчик паники и обработчик паники времени выполнения.
(И помните, что есть также перехватчики паники, мы доберёмся до них.)
Это происходит со мной всё время.
Более того, core::panic! и std::panic! не одинаковы; как мы увидим, они используют совершенно разные пути кода.
libcore и libstd каждый реализуют свой собственный способ вызвать панику:
core::panic! из libcore очень мал: он всего лишь немедленно делегирует панику обработчику.
libstd std::panic! («нормальный» макрос panic! в Rust) запускает полнофункциональный механизм паники, который обеспечивает управляемый пользователем перехват паники.
Хук по умолчанию выведет сообщение о панике в stderr.
После того, как функция перехвата закончена, libstd делегирует её обработчику паники времени выполнения.
libstd также предоставляет обработчик паники, который вызывает тот же механизм, поэтому core::panic! также заканчивается здесь.
Давайте теперь посмотрим на эти части более подробно.
Обработка паники во время выполнения программы
Аргумент usize здесь на самом деле является *mut &mut dyn core::panic::BoxMeUp — это то место, где *mut &mut dyn core::panic::BoxMeUp «полезные данные» паники (информация, доступная при её обнаружении).
Макрос std::panic!
rust_panic_with_hook
Ключевая функция, через которую проходит почти всё, — rust_panic_with_hook :
Эта функция принимает местоположение источника паники, необязательное не отформатированное сообщение (см. Документацию fmt::Arguments ) и полезные данные.
Наконец, rust_panic_with_hook отправляется в текущий обработчик паники времени выполнения.
На данный момент, только payload по — прежнему актуальна — и что важно: message (со временем жизни ‘_ указывает, что могут содержаться короткоживущие ссылки, но полезные данные паники будут распространяться вверх по стеку и следовательно должные быть со временем жизни ‘static ).
Ограничение ‘static там довольно хорошо скрыто, но через некоторое время я понял, что Any подразумевает ‘static (и помните, что dyn BoxMeUp просто используется для получения Box ).
Точки входа в libstd
rust_panic_with_hook — это закрытая функция для std::panicking ; модуль предоставляет три точки входа поверх этой центральной функции и одну, которая её обходит:
Обработчик паники
Макрос core::panic!
Помимо интерфейса обработчика паники, libcore предоставляет минимальный API паники.
core::panic! макрос создаёт fmt::Arguments который затем передаётся обработчику паники.
Здесь не происходит форматирование, так как это потребует выделения памяти в куче; Вот почему PanicInfo содержит «не интерпретированную» строку формата со своими аргументами.
Некоторые элементы API паники в libcore являются элементами языка, потому что компилятор вставляет вызовы этих функций во время генерации кода:
Выводы
Мы прошли через 4 уровня API, 2 из которых были перенаправлены через импортированные вызовы функций и разрешены компоновщиком.
Вот это путешествие!
Но мы достигли конца.
Я надеюсь, что вы не паниковали по пути. 😉
Я упомянул некоторые вещи как удивительные.
Оказывается, все они связаны с тем фактом, что перехватчики паники и обработчики паники разделяют структуру PanicInfo в своём интерфейсе, который содержит как необязательное ещё не отформатированное message и payload со стёртым типом:
Вероятно, есть веские причины против этого предложения и для нынешнего дизайна; было бы здорово получить их где-нибудь в формате документации. 🙂
Есть ещё много чего сказать, но на этом этапе я приглашаю вас перейти по ссылкам на исходный код, который я включил выше.
Имея в виду структуру высокого уровня, вы должны быть в состоянии следовать этому коду.
Если бы люди думали, что этот обзор стоил бы поместить куда-то навсегда, я был бы рад превратить эту статью в блог в какую-то документацию — хотя я не уверен, что это было бы хорошим местом.
И если вы обнаружите какие-либо ошибки в том, что я написал, пожалуйста, дайте мне знать!