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


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


rustup update stable

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


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


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


Улучшение .await с помощью IntoFuture


В Rust 1.64 стабилизирован трейт IntoFuture. Он похож на трейт IntoIterator, но вместо поддержки циклов for ... in ..., IntoFuture изменяет поведение .await. С IntoFuture ключевое слово .await может применяться не только на футурах, но и на всём, что может быть преобразовано в Future при помощи IntoFuture, что сделает ваше API более простым для использования!


Возьмём, к примеру, билдер, который создаёт запрос к некоторому сетевому хранилищу:


pub struct Error { ... }
pub struct StorageResponse { ... }:
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Создать новый экземпляр `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Решить, будет ли включён отладочный режим.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Отправить запрос и получить ответ.
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

Типичное использование, вероятно, будет выглядеть так:


let response = StorageRequest::new()  // 1. Создать экземпляр
    .set_debug(true)                  // 2. Установить некоторые настройки
    .send()                           // 3. Сконструировать Future
    .await?;                          // 4. Запустить Future + пробросить ошибки

Это уже неплохо, но мы можем сделать лучше. Используя IntoFuture, мы можем сделать за один шаг "создание футуры" (строка 3) и "запуск футуры" (строка 4):


let response = StorageRequest::new()  // 1. Создать экземпляр
    .set_debug(true)                  // 2. Установить некоторые настройки
    .await?;                          // 3. Сконструировать + запустить Future + пробросить ошибки

Мы можем сделать это, реализовав IntoFuture для StorageRequest. IntoFuture требует от нас наличия именованной футуры, которую мы возвращаем. Её мы можем создать упакованной и объявить для неё псевдоним:


// Сначала мы должны импортировать в область видимости несколько новых типов.
use std::pin::Pin;
use std::future::{Future, IntoFuture};

pub struct Error { ... }
pub struct StorageResponse { ... }
pub struct StorageRequest(bool);

impl StorageRequest {
    /// Создадим новый экземпляр `StorageRequest`.
    pub fn new() -> Self { ... }
    /// Решим, следует ли включить режим отладки.
    pub fn set_debug(self, b: bool) -> Self { ... }
    /// Отправим запрос и получим ответ
    pub async fn send(self) -> Result<StorageResponse, Error> { ... }
}

// Новая имплементация
// 1. Создадим новый именованный тип футуры
// 2. Имплементируем `IntoFuture` для `StorageRequest`
pub type StorageRequestFuture = Pin<Box<dyn Future<Output = Result<StorageResponse, Error> + Send + 'static>>
impl IntoFuture for StorageRequest {
    type IntoFuture = StorageRequestFuture;
    type Output = <StorageRequestFuture as Future>::Output;
    fn into_future(self) -> Self::IntoFuture {
        Box::pin(self.send())
    }
}

Это требует немного больше кода для реализации, но предоставляет более простой API для пользователей.


Мы надеемся, что в будущем Rust Async WG упростит создание новой именованной футуры при помощи impl Trait в type ("Type Alias Impl Trait" или TAIT). Это должно облегчить реализацию IntoFuture за счёт упрощения сигнатуры псевдонима и улучшить производительность за счёт удаления из псевдонима упаковки (Box).


C-совместимые типы FFI в core и alloc


Вызывая или будучи вызванным C ABI, код Rust может использовать псевдонимы типов, такие как c_uint или c_ulong для сопоставления соответствующих типов из C на любой целевой сборке, не требуя специализированного кода или условий.


Раньше эти псевдонимы типов были доступны только в std, поэтому код, написанный для встроенных систем и других сценариев, которые могли использовать только core или alloc, не мог использовать эти типы.


Rust 1.64 теперь предоставляет все псевдонимы типа c_* в core::ffi, а также core::ffi::CStr для работы со строками C. Rust 1.64 также предоставляет alloc::ffi::CString для работы с собственными строками C, используя только крейт alloc, а не полную библиотеку std.


Rust-analyzer теперь доступен через rustup


Rust-analyzer теперь входит в набор инструментов, включённых в Rust. Это упрощает загрузку и доступ к rust-analyzer, а также делает его доступным на большем количестве платформ. Он доступен как компонент rustup, который можно установить с помощью:


rustup component add rust-analyzer

В настоящее время, чтобы запустить версию, установленную rustup, вам нужно вызвать её следующим образом:


rustup run stable rust-analyzer

В следующем выпуске rustup предоставит встроенный прокси-сервер, так что при запуске исполняемого файла rust-analyzer будет запускаться соответствующая версия.


Большинству пользователей следует продолжать использовать выпуски, предоставленные командой rust-analyzer, которые публикуются чаще. Они доступны на странице выпусков rust-analyzer. Это не затрагивает пользователей официального расширения VSCode, поскольку оно автоматически загружает и обновляет выпуски в фоновом режиме.


Улучшения Cargo: наследование рабочего пространства и многоцелевые сборки


Теперь при работе с коллекциями связанных библиотек или бинарных крейтов в одном рабочем пространстве Cargo, вы можете избежать дублирования общих значений полей между крейтами, таких как общие номера версий, URL-адреса репозитория или rust-version. Это также помогает синхронизировать эти значения между крейтами при их обновлении. Дополнительные сведения см. в workspace.package, workspace.dependencies и "наследование зависимости от рабочего пространства".


При сборке для нескольких целей вы сможете передать несколько опций --target в cargo build, чтобы собрать их все одновременно. Вы также можете установить для build.target массив из нескольких целей в .cargo/config.toml, чтобы по умолчанию выполнять сборку для нескольких целей.


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


Стабилизированы следующие методы и реализации трейтов:



Типы, ранее стабилизированные в std::ffi и теперь доступные в core и alloc:



Типы, ранее стабилизированные в std::os::raw и теперь доступные в core::ffi и std::ffi:



Были стабилизированы несколько вспомогательных функций, которые используются с Poll, низкоуровневой реализацией футур:



Мы надеемся, что в будущем сможем предоставить более простое API, которое будет требовать меньше использования низкоуровневых деталей, таких как Poll и Pin. Но сейчас данные вспомогательные функции упростят написание такого кода.


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



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


  • Как было ранее анонсировано, для компиляции целей linux потребуется ядро Linux 3.2 и выше (за исключением тех целей, которые уже требуют более новое ядро). Также цели linux-gnu теперь требуют glibc 2.17 (за исключением тех, которые уже требуют более новую glibc).
  • Rust 1.64.0 изменяет структуру размещения в памяти Ipv4Addr, Ipv6Addr, SocketAddrV4 и SocketAddrV6, чтобы сделать её более компактной и эффективной. Это внутреннее представление никогда не раскрывалось, но некоторые крейты всё равно полагались на него, используя std::mem::transmute, что приводило к недопустимым обращениям к памяти. Такие детали внутренней реализации стандартной библиотеки никогда не считаются стабильным интерфейсом. Чтобы ограничить ущерб, мы работали с авторами всех ещё поддерживаемых крейтов, чтобы выпустить исправленные версии, которые отсутствовали более года. Подавляющее большинство затронутых пользователей будут иметь возможность смягчить последствия с помощью cargo update.
  • В рамках прекращения поддержки RLS это также последний выпуск, содержащий копию RLS. Начиная с Rust 1.65.0, RLS будет заменён небольшим сервером LSP, показывающим предупреждение об устаревании.

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


Выпуск Rust 1.64 включает и другие изменения:


  • Компилятор Rust для Windows теперь собирается с PGO, что даёт прирост производительности при компилировании кода на 10-20%.
  • Если вы объявляли структуру, содержащую поле, которое никогда не используется, то rustc об этом предупреждал. Теперь вы можете включить проверку unused_tuple_struct_fields, чтобы получить аналогичное предупреждение о неиспользуемых полях в кортежных структурах. В будущих версиях мы планируем включить эту проверку по умолчанию. Для полей единичного типа (()) такое предупреждение не будет генерироваться для облегчения миграции существующего кода без изменения индексов кортежа.

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


Участники 1.64.0


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


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


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


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

Комментарии (3)


  1. DarkEld3r
    24.09.2022 11:48
    +2

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

    Кто-нибудь может объяснить мне эту часть? Получается, что если есть кортеж вида (u8, (), u16) и "среднее" поле не используется, то предупреждения не будет?.. Но к чему тут "облегчения миграции существующего кода без изменения индексов кортежа"?


    1. mayorovp
      24.09.2022 12:04
      +8

      Ну вот у вас был кортеж вида (u8, u8, u16), и оказалось что второй элемент нигде не используется. Если вы его удалите и получите (u8, u16) — вам придётся всюду переделывать обращение к третьему элементу на обращение ко второму. Поэтому предлагается использовать вариант (u8, (), u16), который столь же оптимален по памяти — но не требует переделывания обращений.


      1. DarkEld3r
        24.09.2022 12:29
        +2

        Теперь дошло, спасибо за объяснение!


        Хотя я бы всё-таки поправил индексы, если это, конечно, не публичное апи, которое не хочется лишний раз ломать.