11月12日、Rust公式ブログが「Announcing Rust 1.100.0」と題した記事を公開した。この記事では、Rust 1.100.0の新機能と安定化された各種APIについて詳しく紹介されている。以下に、その内容を紹介する。
バージョン1.100という節目のリリースで、長年の懸案事項がいくつか一気に片付いた。中でも最大のトピックは、10年越しでついに安定化されたNever型だ。
10年・5回の失敗を経てNever型が安定化
Never型(コード上では!と書く)は、「決して値を返さない計算」を表す型だ。returnやbreak、std::process::exitといった制御フローを終了させる式はすべてこの!型を持つ。
Rustではこれまでも!を関数の戻り型(-> !)として使うことはできた。しかし1.100.0では、任意の場所でNever型を名指しできるようになった。
fn infallible_unwrap<T>(res: Result<T, !>) -> T {
// ^^ `Result<_, !>` と書けなかったが、今はできる
let Ok(v) = res;
v
}
あわせて、既存のInfallible型がNever型のエイリアスに変更された。これにより、Infallibleを使っていた従来のAPIがそのまま!と互換になる。これはもともとInfallible導入時からの計画だったが、実現までに多くの困難があった(waffleによる安定化の苦労話の講演も公開されている)。
なお、この変更に伴い破壊的変更が1点ある。Edition 2024より前(2015・2018・2021)においても、"never type fallback"が()から!に変わる。ほとんどのコードには影響しないが、ジェネリックパラメータを返す関数の結果を無視しているケースなどでは影響が出る可能性がある。将来互換警告は事前に提供されており、影響を受ける公開クレートへの修正もすでにリリース済みのものが多い。
DropGuard:全終了パスで確実にクロージャを実行
RustのDropトレイトはスコープを抜けた際に自動でコードを走らせる仕組みだが、「単純に関数終了時に何かしたい」という用途では実装がやや面倒だった。
1.100.0ではcore::mem::DropGuardが新たに追加された。変数とクロージャを受け取り、ドロップ時にそのクロージャを実行するヘルパー型だ。Deref・DerefMutを実装しているため、ラップした変数を普通に使い続けられる。
またdismissメソッドでガードを解除すれば、デストラクタを走らせずに変数を取り出せる。これを使うと「失敗時にロールバック、成功時はそのまま」というトランザクション的なAPIを簡潔に書ける。
use std::mem::DropGuard;
use std::num::ParseIntError;
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. 失敗時にVecをリセット
});
for s in strings {
nums.push(s.parse()?); // ← 2. `?`でエラー時にロールバック
}
DropGuard::dismiss(nums); // ← 3. 成功したのでガードを解除
Ok(())
}
Allocator APIの安定化
Allocatorトレイトを中心としたallocator_apiのコア部分が安定化された。現時点ではVecとBoxでカスタムアロケータを利用できる。Allocators 1.0プロジェクトゴールの基盤となる変更で、今後さらに対応するコレクションや型が拡張される見込みだ。
ゼロサイズ型を安全に生成するconjure_zst
std::mem::conjure_zstが追加された。ゼロサイズ型(ZST)の値を生成する専用関数で、従来はMaybeUninit::uninit().assume_init()などの"呪文"に頼っていた部分を明示的に扱えるようになった。ただし、ZSTを使って安全性の前提条件を表現しているAPIを破壊しうるためunsafe関数として提供されている。
Cargoの改善:未使用依存関係の警告・依存キャッシュ衝突の軽減・依存クールダウン
未使用依存関係の警告
Cargoが未使用の依存関係を検出して警告を出すようになった。
warning: unused dependency `tracing-subscriber`
--> Cargo.toml:23:1
|
23 | tracing-subscriber.workspace = true
| ^^^^^^^^^^^^^^^^^^
ただし、依存テーブルのすべてのビルドターゲットをビルドした場合のみ表示される制約がある。cargo check --bin fooのような部分ビルドでは警告が出ない点に注意が必要だ。
キャッシュ衝突の軽減
ビルドディレクトリ(build-dir)内のコンテンツ管理方法が変わり、ビルド設定ハッシュが一貫して適用されるようになった。git worktreeをまたいだビルドキャッシュの共有など、将来的な改善への布石でもある。内部的な変更だが、既存のビルドプロセスやテストに影響が出る可能性があり、cargo#17287に対処法がまとめられている。
依存クールダウン(min-publish-age)
サプライチェーン攻撃への対策として、新しく公開されたバージョンを一定期間スキップする機能が安定化された。.cargo/config.tomlに以下を書くだけで有効になる。
[registry]
global-min-publish-age = "3 days"
悪意あるパッケージは公開後数時間〜数日以内にコミュニティや自動スキャナで検出されることが多い。この仕組みでその検出窓を活かせる。なお、どうしてもすぐ新バージョンが必要な場合は環境変数CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allowで個別に上書きできる。
詳細はAnnouncing Rust 1.100.0を参照していただきたい。