[Перевод] Rust 1.78.0: Диагностические атрибуты, проверка предусловий unsafe и детерминированное повторное выравнивание

fc3fb2ed82a37dccb1def06776bae641

Команда Rust рада сообщить о новой версии языка — 1.78.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.

Если у вас есть предыдущая версия Rust, установленная через rustup, то для обновления до версии 1.78.0 вам достаточно выполнить команду:

$ rustup update stable

Если у вас ещё не установлен rustup, вы можете установить его с соответствующей страницы нашего веб-сайта, а также посмотреть подробные примечания к выпуску на GitHub.

Если вы хотите помочь нам протестировать будущие выпуски, вы можете использовать канал beta (rustup default beta) или nightly (rustup default nightly). Пожалуйста, сообщайте обо всех встреченных вами ошибках.


Что стабилизировано в 1.78.0


Диагностические атрибуты

Теперь Rust поддерживает пространство имён атрибутов #[diagnostic], что позволяет влиять на сообщения об ошибоках компилятора. Теперь они рассматриваются как подсказки, которые компилятору не должен использовать. Также это не ошибка, предоставляющая диагностику, которую компилятор не распознаёт. Такая гибкость позволяет исходному коду предоставлять диагностику, даже если она не поддерживается всеми компиляторами, будь то разные версии или совершенно разные реализации компилятора.

В этом пространстве имён появляется первый поддерживаемый атрибут #[diagnostic::on_unimplemented], который можно применить к трейту для настройки сообщения, которое отобразится, когда этот трейт требуется, но не реализован у типа. Рассмотрим пример, приведённый в запросе на стабилизацию:

#[diagnostic::on_unimplemented(
    message = "My Message for `ImportantTrait<{A}>` is not implemented for `{Self}`",
    label = "My Label",
    note = "Note 1",
    note = "Note 2"
)]
trait ImportantTrait {}

fn use_my_trait(_: impl ImportantTrait) {}

fn main() {
    use_my_trait(String::new());
}

Ранее компилятор выдал бы такую ​​встроенную ошибку:

error[E0277]: the trait bound `String: ImportantTrait` is not satisfied
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ the trait `ImportantTrait` is not implemented for `String`
   |     |
   |     required by a bound introduced by this call
   |

Теперь с помощью #[diagnostic::on_unimplemented] пользовательское сообщение заполняет основную строку ошибки, а пользовательская метка помещается в исходный вывод. Исходная метка по-прежнему записывается как справочная информация, а также записываются все пользовательские примечания (эти данные могут быть изменены).

error[E0277]: My Message for `ImportantTrait` is not implemented for `String`
  --> src/main.rs:12:18
   |
12 |     use_my_trait(String::new());
   |     ------------ ^^^^^^^^^^^^^ My Label
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `ImportantTrait` is not implemented for `String`
   = note: Note 1
   = note: Note 2

Для авторов типажей такой вид диагностики более полезен, потому что с его помощью можно дать более подробную подсказку, чем просто писать об отсутствующей реализации. В качестве примера ниже показан сокращённый образец из стандартной библиотеки:

#[diagnostic::on_unimplemented(
    message = "the size for values of type `{Self}` cannot be known at compilation time",
    label = "doesn't have a size known at compile-time"
)]
pub trait Sized {}

Дополнительные сведения смотрите в справочном разделе о пространстве имён атрибута diagnostic.


Проверка предусловий unsafe

Стандартная библиотека Rust содержит ряд утверждений для предварительных условий unsafe функций, но исторически они были включены только в сборках стандартной библиотеки при помощи #[cfg(debug_assertions)], чтобы избежать влияния на производительность release сборки. Однако, поскольку стандартная библиотека обычно компилируется и распространяется с release профилем, большинство разработчиков Rust вообще никогда не выполняют эти проверки.

Теперь выполнение условий для этих проверок откладывается до генерации кода, поэтому они будут проверяться в зависимости от собственных настроек пользователя для отладочных проверок, включённых по умолчанию в отладочных и тестовых сборках. Это изменение помогает пользователям отслеживать неопределённое поведение в своём коде, хотя информация о том, сколько всего проверяется, как правило, не является стабильной.

Например, slice::from_raw_parts требует выровненный ненулевой указатель. Следующее использование намеренно смещённого указателя приводит к неопределённому поведению и, хотя это может не иметь очевидных последствий, debug-проверка теперь может перехватить его:

fn main() {
    let slice: &[u8] = &[1, 2, 3, 4, 5];
    let ptr = slice.as_ptr();

    // Создание отступа от `ptr` что всегда будет единственным отличием от корректного смещения `u16`
    let i = usize::from(ptr as usize & 1 == 0);

    let slice16: &[u16] = unsafe { std::slice::from_raw_parts(ptr.add(i).cast::(), 2) };
    dbg!(slice16);
}
thread 'main' panicked at library/core/src/panicking.rs:220:5:
unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread caused non-unwinding panic. aborting.


Детерминированное повторное выравнивание

В стандартной библиотеке есть несколько функций, которые изменяют выравнивание указателей и срезов. Раньше в их использовании было несколько оговорок, из-за которых на практике полагаться на них было трудно, особенно если вы точно следовали документации. Эти предостережения в первую очередь существовали как страховка от вычисления const, но в любом случае они были стабильны только для использования вне const. Теперь обещано, что они будут иметь согласованное поведение во время выполнения в соответствии с фактическими входными данными


  • pointer::align_offset вычисляет смещение, необходимое для изменения указателя на заданное выравнивание. Эта функция возвращает usize::MAX, если смещение невозможно, хотя раньше ей было разрешено всегда возвращать usize::MAX. Теперь такое поведение удалено.


  • Функции slice::align_to и slice::align_to_mut преобразуют срезы в выровненный срез средней части данных и невыровненные головной и хвостовой. Теперь они обещают возвращать максимально возможную среднюю часть — вместо того, чтобы позволять реализации возвращать что-то менее оптимальное. Например, возвращать всё в виде головного среза.



Стабилизированные API

Следующие API теперь можно использовать в контексте const:


Замечания о совместимости


  • Как ранее анонсировалось, в Rust 1.78 поднимается минимальное требование до Windows 10 для следующих целевых платформ:
    • x86_64-pc-windows-msvc
    • i686-pc-windows-msvc
    • x86_64-pc-windows-gnu
    • i686-pc-windows-gnu
    • x86_64-pc-windows-gnullvm
    • i686-pc-windows-gnullvm
  • В Rust 1.78 обновлён встроенный LLVM до версии 18, завершено изменение ABI для u128/i128 для x86–32 и x86–64 платформ. Дистрибьюторы, которые используют свою собственную LLVM старше 18 лет, всё ещё могут сталкиваться с ошибками в соглашении о вызовах, упомянутыми в этом посте.


Прочие изменения

Проверьте всё, что изменилось в Rust, Cargo и Clippy.


Кто работал над 1.78.0

Многие люди собрались вместе, чтобы создать Rust 1.78.0. Без вас мы бы не справились. Спасибо!


От переводчиков

С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.

Данную статью совместными усилиями перевели blandger, andreevlex, TelegaOvoshey и funkill.

© Habrahabr.ru