openlang

Rust 2024 наконец поставил unsafe на блок extern

Rust 2024 требует unsafe extern "C" { ... } вместо голого блока extern, и это самое честное небольшое изменение синтаксиса в языке за последние годы. Объявление внешней функции всегда было непроверенным утверждением о её сигнатуре. Ошибитесь в типах аргументов, и каждый вызов будет неопределённым поведением, как бы тщательно ни проверяли место вызова. Старый синтаксис ставил unsafe только в местах вызова и отправлял рецензентов смотреть не на те строки.

Теперь внутри блока элемент можно пометить safe fn, если у внешней функции действительно нет предусловий, и тогда вызывающему коду unsafe вообще не нужен. Вся ответственность переходит на одно объявление, и именно там ей место на ревью.

Та же редакция делает unsafe_op_in_unsafe_fn предупреждением по умолчанию, так что тело unsafe fn больше не считается одним большим небезопасным блоком. Каждой операции нужен свой unsafe { } и, по соглашению, комментарий SAFETY с объяснением, почему всё корректно.

Следующим шагом должно стать обязательное обоснование для каждого safe fn во внешнем блоке. С этим справился бы линт.

Разговор продолжается 4

Следуя доводам
  1. Ссылка на ответ

    Carbon целится в противоположный конец. План в том, чтобы код на Carbon напрямую импортировал заголовки C++ без написанных вручную внешних объявлений, так что неверную сигнатуру просто негде набрать. Выдержит ли это история с безопасностью, пока открытый вопрос. Проект сам называет себя экспериментальным, и дизайн безопасности памяти не закончен, поэтому всё сказанное сегодня о корректности взаимодействия Carbon с C++ это намерение, а не поведение.

  2. Ссылка на ответ

    Сигнатура это половина дела. Вторая половина это раскрутка стека. Паника, пытающаяся выйти из Rust-функции extern "C", теперь прерывает процесс, а исключение C++, переходящее в Rust, определено только через ABI "C-unwind". Значит, объявление должно ещё и говорить, может ли через него что-то раскручиваться. Какой уже созданный объект владеет дескриптором в этот момент? При аварийном завершении ни один деструктор не выполнится.

  3. Ссылка на ответ

    У этого есть почти близнец в проверке Sendable в Swift. Заголовки C ничего не говорят о том, какой поток что может трогать, поэтому обёртку над C-дескриптором обычно помечают @unchecked Sendable, и компилятор верит автору на слово, почти как с safe fn во внешнем блоке. Такие пометки лучше держать редкими и легко находимыми через grep, с комментарием о том, какая блокировка или очередь делает это правдой. А @preconcurrency import тихо ослабляет диагностику Sendable для модуля, что нормально во время миграции и легко забывается потом.

  4. Ссылка на ответ

    Не совсем одно и то же. @unchecked Sendable ручается за тип везде, куда он попадёт, а safe fn ручается за предусловия одной внешней функции. И то и другое должно легко находиться через grep, и Rust хотя бы заставляет написать слово unsafe на блоке, где лежит это поручительство, так что один поиск находит все такие утверждения.

Другие стороны того же вопроса

  1. Небольшая правка пунктуации может затронуть три слоя языка

    Авторы правил хотят писать age>=18=>allow, без пробелов и скобок. На вид изменение крошечное. Но на каком этапе решается, что…