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


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


rustup update stable

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


Что вошло в стабильную версию 1.41.0


Основными новшествами Rust 1.41.0 являются ослабление ограничений на реализацию типажей, улучшения cargo install, новый формат файла Cargo.lock более дружелюбный для работы с git, и новые гарантии для Box<T>, связанные с FFI. Смотрите подробности выпуска для дополнительной информации.


Ослабление ограничений при реализации типажей


Для предотвращения поломок в экосистеме, когда зависимость добавляет новые реализации типажа, Rust использует правило сироты (orphan rule). Суть в том, что реализация типажа допустима только если типаж или тип, который его реализует, является локальным, т.е. определённым в текущем крейте. Однако это достаточно сложно, когда используются обобщения.


До версии Rust 1.41.0 это правило было слишком строгим, мешая композиции. Например, предположим что ваш пакет реализует структуру BetterVec<T>, и вы хотите иметь возможность конвертации его в Vec<T> из стандартной библиотеки. Вы бы написали следующий код:


impl<T> From<BetterVec<T>> for Vec<T> {
    // ...
}

… который является примером паттерна:


impl<T> ForeignTrait<LocalType> for ForeignType<T> {
    // ...
}

В версии Rust 1.40.0 этот impl был запрещён правилом сироты, так как From и Vec определены в стандартной библиотеке, которая является чужим крейтом по отношению к текущему крейту. Были способы обойти это ограничение, такие как шаблон newtype, но они часто были громоздкими или даже невозможными в некоторых случаях.


Хотя From и Vec всё ещё остаются чужими, типаж (в данном случае From) параметризован локальным типом. Поэтому, Rust 1.41.0 позволяет этот impl.


Для более подробной информации читайте отчёт о стабилизации и предложение RFC.


cargo install обновляет пакеты если они устарели


При помощи cargo install вы можете установить в систему исполняемый крейт. Эта команда часто используется для установки популярных CLI-инструментов, созданных комьюнити и написанных на Rust.


Начиная с Rust 1.41.0, cargo install также может обновлять установленные крейты, если с момента установки появился новый релиз. До этого выпуска единственным возможным вариантом было использование флага --force, который позволял переустановить исполняемый крейт даже если он не нуждался в обновлении.


Менее конфликтный формат Cargo.lock


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


Rust 1.41.0 представляет новый формат для этого файла, разработанный специально для уменьшения конфликтов. Новый формат будет использоваться для всех новых lock-файлов, в то время как существующие файлы будут использовать предыдущий формат. Узнать больше о вариантах, которые привели к новому формату, вы можете в PR, в котором его добавили.


Больше гарантий при использовании Box<T> в FFI


Начиная с Rust 1.41.0, мы заявляем, что Box<T>, когда T: Sized, теперь совместим по ABI с типами указателей в C (T*). Таким образом, если вы определяете функцию extern "C" в Rust и вызываете её из C, ваша функция в Rust теперь может использовать Box<T> для какого-то T, и использовать T* в соответствующей функции на C. В качестве примера, на стороне C вы можете иметь:


// C header */

// Возвращаем владение в вызывающий код
struct Foo* foo_new(void);

// Принимает владение из вызывающего кода; no-op если передан NULL.
void foo_delete(struct Foo*);

… а на стороне Rust у вас будет:


#[repr(C)]
pub struct Foo;

#[no_mangle]
pub extern "C" fn foo_new() -> Box<Foo> {
    Box::new(Foo)
}

// Возможность принимать NULL реализуется с помощью `Option<_>`.
#[no_mangle]
pub extern "C" fn foo_delete(_: Option<Box<Foo>>) {}

Заметим однако, что несмотря на то, что Box<T> и T* имеют одинаковое представление и ABI, Box<T> обязательно должен быть не-null, выровнен и быть готовым для деаллокации глобальным аллокатором. Чтобы обеспечить эти требования, самое лучшее — это использовать такие Box'ы, которые порождаются глобальным аллокатором.


Важно: По крайней мере в настоящее время вы должны избегать использования типов Box<T> для функций, которые определены в C, но вызываются из Rust. В этих случаях вы должны отразить типы как можно ближе к определению в C. Использование типов наподобие Box<T>, когда определение в C использует просто T* может привести к неопределённому поведению.


Чтобы узнать больше, обратитесь к документации Box<T>.


Изменения в библиотеке


В версии Rust 1.41.0, мы сделали следующие изменения в стандартной библиотеке:



Сокращение поддержки 32-битной целевой платформы Apple


Rust 1.41.0 будет последним выпуском с текущим уровнем поддержки 32-битных платформ Apple, включая i686-apple-darwin. Начиная с Rust 1.42.0 эти платформы будут понижены до самого низкого уровня поддержки.


Узнать об этом больше вы можете в соответствующей записи в блоге.


Другие изменения


Синтаксис, пакетный менеджер Cargo и анализатор Clippy также претерпели некоторые изменения. Мы также приступили к внедрению оптимизаций MIR, которые должны ускорить компиляцию: вы можете узнать о них в блоге "Inside Rust".


Участники 1.41.0


Множество людей собрались вместе, чтобы создать Rust 1.41.0. Мы не смогли бы сделать это без всех вас, спасибо!


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


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


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