Команда Rust рада сообщить о новой версии языка — 1.100.0. Rust — это язык программирования, позволяющий каждому создавать надёжное и эффективное программное обеспечение.
Если у вас есть предыдущая версия Rust, установленная через rustup, то для обновления до версии 1.100.0 вам достаточно выполнить команду:
$ rustup update stable
Если у вас ещё не установлен rustup, вы можете установить его с соответствующей страницы нашего веб-сайта, а также посмотреть подробные примечания к релизу 1.100.0.
Если вы хотите помочь нам протестировать будущие релизы, вы можете использовать канал beta (rustup default beta) или nightly (rustup default nightly). Пожалуйста, сообщайте обо всех встреченных вами ошибках!
Что стабилизировано в 1.100.0
Тип «never»
Спустя 10 лет и 5 неудачных попыток стабилизации тип «never» наконец-то стабилизирован! Это также завершает соответствующий проект.
Тип «never», записываемый в коде как !, — это канонический ненаселённый тип (uninhabited type). Он представляет тип расходящихся вычислений — таких, которые никогда не возвращают какое-либо значение, а любой следующий за ними код является недостижимым. Выражения, прерывающие поток управления, такие как return, break и std::process::exit, также имеют тип !.
Поскольку существование значения типа «never» невозможно, Rust разрешает неявное приведение типа ! к любому другому типу (именно это отличает его от прочих ненаселённых типов). Это можно использовать, например, чтобы не возвращать значение из одной из ветвей match или if:
fn mrrrow(option: Option<u32>) { let value = match option { // `x` имеет тип `u32` Some(x) => x, // `return` имеет тип `!`, который затем приводится к `u32`, // позволяя выражению `match` пройти проверку типов. None => return, }; // ... }
Хотя тип «never» уже можно было использовать таким образом, и даже указывать в возвращаемом типе функций (-> !), в этом релизе разрешается явно указывать тип «never» в любом месте:
fn infallible_unwrap<T>(res: Result<T, !>) -> T { // ^^ запись `Result<_, !>` ранее не допускалась let Ok(v) = res; v }
Кроме того, в этом релизе тип Infallible превращён в псевдоним (alias) для типа «never». Это делает все старые API, использовавшие Infallible, совместимыми с !. Такой план существовал с самого момента появления Infallible, и именно это стало одной из причин, почему стабилизация типа «never» оказалась столь сложной (waffle выступал с докладом о некоторых трудностях этой стабилизации).
К сожалению, чтобы сделать возможным изменение Infallible, нам пришлось внести обратно несовместимое изменение. В этом релизе резервный тип для типа «never» (never type fallback) изменён с () на ! в редакциях языка до 2024 (то есть 2015, 2018 и 2021), аналогично тому, как это [уже было сделано в редакции 2024]. В подавляющем большинстве случаев это пройдёт незаметно, однако в некоторых редких ситуациях может затронуть существующий код (например, при игнорировании результата функции, возвращающей обобщённый параметр). Обратите внимание, что из-за этого изменения компилятор также может начать распознавать больше участков кода как недостижимые.
Мы постарались максимально смягчить последствия этого изменения, заранее добавив предупреждения о будущей совместимости и выпустив исправления для публичных крейтов, которые оказались затронуты. Если сборка вашей зависимости падает из-за проблем с типом «never», попробуйте обновить её — вероятно, исправление для неё уже опубликовано.
API Allocator
В этом релизе стабилизированы основные части allocator_api, что позволяет авторам библиотек определять пользовательские аллокаторы и использовать их с некоторыми типами стандартной библиотеки — на данный момент с Vec и Box. Эта возможность реализуется через недавно стабилизированный трейт Allocator.
Это закладывает основу для проекта «Allocators 1.0». Впереди ещё много работы, включая расширение API, совместимого с пользовательскими аллокаторами, и стабилизацию их поддержки для большего числа коллекций и типов указателей; однако теперь, когда базовый трейт и архитектура готовы, в скором времени стоит ожидать стабилизации и остальных возможностей!
DropGuard: запуск замыкания при любом выходе из функции
Трейт Drop в Rust позволяет выполнять код при выходе переменной из области видимости независимо от того, происходит ли выход через return, ? или панику. Наличие Drop в системе типов означает, что в Rust вам никогда не придётся вручную закрывать сокет.
Однако реализация Drop может показаться несколько громоздкой, когда всё, что вам нужно, — это безусловно выполнить какой-либо код при выходе из функции. Поэтому мы представляем core::mem::DropGuard — новый вспомогательный тип, который вызывает замыкание при удалении переменной.
DropGuard принимает два аргумента: переменную и замыкание. Он реализует Deref и DerefMut, поэтому вы можете продолжать работать с переменной как обычно. Но при удалении он запускает замыкание и поглощает переменную.
DropGuard также реализует метод dismiss, который извлекает переменную без вызова деструктора. Это позволяет быстро реализовывать транзакционные API по принципу «всё или ничего» с поддержкой отката. Ниже приведён пример парсера, который использует DropGuard для отката изменений в случае сбоя:
use std::mem::DropGuard; use std::num::ParseIntError; /// Пытается разобрать все строки из `strings` и добавить их в `nums`. /// Если разбор любой из строк завершится ошибкой, `nums` останется без изменений. fn parse_all(nums: &mut Vec<u32>, strings: &[&str]) -> Result<(), ParseIntError> { let len = nums.len(); let mut nums = DropGuard::new(nums, move |nums| { nums.truncate(len); // ← 1. Сброс вектора при ошибке }); for s in strings { nums.push(s.parse()?); // ← 2. Если `?` вернёт ошибку, выполняется откат } DropGuard::dismiss(nums); // ← 3. Разбор прошёл успешно, отключаем DropGuard Ok(()) }
Создание типов нулевого размера
Поскольку работа над руководством по небезопасному коду в Rust всё ещё продолжается, стало очевидно, что было бы крайне полезно чётко определить, что именно означает небезопасное создание типов нулевого размера (zero-sized types) и какой способ для этого следует рекомендовать разработчикам.
Ответ на этот вопрос в Rust 1.100 даёт единственная функция: std::mem::conjure_zst.
Вместо преобразования типов через transmute для (), MaybeUninit::uninit().assume_init() и других подобных ухищрений теперь существует специализированный способ создать значение типа нулевого размера, когда вы уверены в безопасности этого действия. Если населённый тип не имеет размера, мы точно знаем, каким должно быть его значение: ничем.
Разумеется, многие API полагаются на невозможность сконструировать такие типы для обеспечения безопасности, и именно поэтому данная функция является небезопасной (unsafe). Например, Python<'py> в PyO3 — это маркерный токен нулевого размера, удостоверяющий удержание GIL (глобальной блокировки интерпретатора Python). Для безопасного вызова многих API Python требуется удержание этой блокировки, а «сотворение» токена из воздуха позволило бы безопасному коду на PyO3 нарушить это предусловие.
Сообщения о неиспользуемых зависимостях в Cargo
Сборка зависимостей, которые больше не используются или нужны только для определённых платформ, может замедлять процесс компиляции. В rustc для этого существует статическая проверка (lint) unused_crate_dependencies, однако при использовании совместно с Cargo она даёт ложные срабатывания, поскольку зависимости могут разделяться между несколькими целями сборки — например, dependencies.tracing-subscriber доступна для каждого исполняемого файла (bin) в пакете, хотя реально её используют лишь некоторые из них.
Теперь Cargo сам сообщает пользователям о неиспользуемых зависимостях, например:
warning: unused dependency `tracing-subscriber` --> Cargo.toml:23:1 | 23 | tracing-subscriber.workspace = true | ^^^^^^^^^^^^^^^^^^ | = note: `cargo::unused_dependencies` is set to `warn` by default help: consider removing the dependency on `tracing-subscriber`
Ограничение этого предупреждения заключается в том, что оно появляется только при сборке всех целей, которые потенциально могут использовать запись из таблицы зависимостей. Например, запуск cargo check --bin foo не выведет предупреждений о неиспользуемых [dependencies], так как в пакете может существовать другой bin, который их задействует. Для неиспользуемых dev-dependencies предупреждения выводиться не будут, поскольку нет способа гарантировать, что собраны все цели разработки.
Это предупреждение добавлено в рамках новой системы статических проверок (линтов) для Cargo, управляемой через таблицу [lints.cargo] в Cargo.toml, что завершает проект стабилизации системы проверок Cargo. Ознакомьтесь также с другими добавленными проверками.
Снижение количества коллизий кэша в Cargo
Cargo собирает все библиотеки и исполняемые файлы в каталоге build-dir, а затем перемещает (uplift) итоговые артефакты в target-dir. В настоящее время эти каталоги имеют общую родительскую директорию. Различные конфигурации сборки, например с флагом --features, могут приводить к появлению разных копий библиотек и исполняемых файлов в build-dir. Уникальность этих копий обеспечивается хэшем конфигурации сборки, однако из-за платформенных ограничений этот хэш применялся не всегда. Это могло вызывать лишние пересборки, пропущенные пересборки, путаницу с тем, какие именно бинарные файлы или библиотеки используются, и так далее. Эти проблемы особенно обостряются при совместном использовании build-dir между несколькими рабочими деревьями Git (git worktrees).
Теперь build-dir последовательно применяет хэш конфигурации сборки ко всему содержимому, организуя каталоги по шагу сборки, а не по типу файлов, и перенося хэш из имени файла в имя родительского каталога.
Эта работа началась в 2025 году в рамках проекта по структуре каталога сборки, а её стабилизация стала важной вехой на пути к новому проекту «Cargo cross workspace cache», открывающему возможности для будущих улучшений, таких как сборка мусора в кэше, дедупликация кэша между воркспейсами, кросс-воркспейс кэширование и гранулярные блокировки.
Ранее мы просили протестировать изменения, которые теперь вступают в силу. Хотя это внутреннее изменение, оно может нарушить работу сборочных процессов и тестов, в которых использовались обходные пути для отсутствующих или неполных возможностей. В некоторых случаях такие обходные пути больше не требуются. Возможные решения и описание новых сценариев использования можно найти в задаче cargo#17287.
Настройка min-publish-age в Cargo (также известная как задержка обновления зависимостей)
Вредоносные версии пакетов нередко обнаруживаются в течение нескольких часов или дней после публикации — либо автоматическими сканерами, либо силами сообщества. В Rust 1.100 стабилизировано разрешение зависимостей Cargo с учётом параметра min-publish-age, который позволяет выждать это временное окно перед тем, как подтягивать свежеопубликованные версии. Эта практика также известна как «период охлаждения» зависимостей (dependency cooldowns).
Вы можете включить эту функцию в файле .cargo/config.toml:
[registry] global-min-publish-age = "3 days"
Теперь при выборе версий Cargo будет пропускать любые версии, опубликованные позже указанного срока, и сообщать о том, что было отложено:
$ cargo update Updating crates.io index Locking 2 packages to highest compatible version as of min-publish-age Adding cc v1.11.0 (available: v1.18.0, published 3 hours ago) Adding clap 4.6.7 (available: 4.6.9, published 2 days ago)
Если новая версия требуется немедленно, вы можете переопределить эту проверку для одного конкретного обновления:
$ CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow cargo update clap --precise 4.6.9 Updating crates.io index Updating clap 4.6.7 -> clap 4.6.9 (published 2 days ago, minimum age 3 days)
Более новая версия будет записана в Cargo.lock и сохранится там до тех пор, пока последующий запуск cargo update не пересчитает зависимости заново.
Несколько моментов, которые стоит иметь в виду:
Работа функции зависит от того, фиксирует ли реестр пакетов время публикации каждой версии. Версии, у которых в индексе отсутствует поле
pubtime, никогда не задерживаются.Команда
cargo installигнорирует минимальный возраст публикации как для самого устанавливаемого крейта, так и для его зависимостей. При необходимости зафиксируйте версию с помощьюcargo install foo@1.2.3.«Период охлаждения» снижает риски некоторых атак на цепочку поставок (supply chain attacks), но сам по себе не является полной защитой. Пожалуйста, продолжайте проверять обновления зависимостей так же тщательно, как и раньше.
Дальнейшая работа по защите экосистемы запланирована в рамках проекта по усилению безопасности цепочки поставок crates.io, включая поддержку карантина и автоматического обнаружения подозрительных релизов.
Стабилизированные API
Прочие изменения
Проверьте всё, что изменилось в Rust, Cargo и Clippy.
Кто работал над 1.100.0
Многие люди собрались вместе, чтобы создать Rust 1.100.0. Без вас мы бы не справились. Спасибо!
От переводчиков
С любыми вопросами по языку Rust вам смогут помочь в русскоязычном Телеграм-чате или же в аналогичном чате для новичковых вопросов. Если у вас есть вопросы по переводам или хотите помогать с ними, то обращайтесь в чат переводчиков.
