10月1日、Rust公式ブログが「Announcing Rust 1.99.0」と題した記事を公開した。今回のリリースでは、長年Rustエコシステムの懸案だった「C ABIの可変引数関数をRust側で実装する」機能がstableに昇格したほか、ローレベルメモリ操作に関わる複数のAPIが安定化された。
Rustはシステムプログラミング言語として、既存のCライブラリとの連携(FFI: Foreign Function Interface)が欠かせない。しかしこれまで、Cの可変引数関数(printf のように引数の数や型が呼び出し側に委ねられる関数)をRust側で実装することはできなかった。そのため、CライブラリへのRustラッパーを書く際にはC側にスタブを用意せざるを得ないケースがあり、ビルドの複雑さという形で摩擦が残り続けていた。1.99.0はその問題に決着をつける節目のリリースだ。
目玉機能:extern "C" 可変引数関数の定義がstable化
Rustは以前から、Cライブラリの可変引数関数(例:libc::printf)を呼び出すことは可能だった。1.99.0では、そういった関数をRust側で実装することもできるようになった。
/// SAFETY: must be called with (at least) 2 i32 arguments.
unsafe extern "C" fn sum(mut args: ...) -> i32 {
// SAFETY: guaranteed by the caller.
let a = unsafe { args.next_arg::<i32>() };
let b = unsafe { args.next_arg::<i32>() };
a + b
}
fn foo() -> i32 {
unsafe { sum(0i32, 2i32) }
}
... の型は VaList(Cの va_list 型とABI互換)で、読み出せる型は VaArgSafe トレイトで制約されている。対応ABIは "C" と "C-unwind" の2種類。また、インラインアセンブリを用いた非"C" ABIのnaked可変引数関数の定義もあわせてstable化された。
この機能はnightlyでは長らく #![feature(c_variadic)] として実験的に提供されており、トラッキングissueは2017年にオープンされたものだ。約9年越しのstable化となる。CライブラリとのFFIを多用するシステムプログラミングの現場では、Cラッパーなしで完結したFFI実装を書けるようになることで、ビルドシステムの構成が大幅にシンプルになる。
生ポインタからレイアウト情報を取得する3関数がstable化
unsized型(スライスやトレイトオブジェクトなど、コンパイル時にサイズが確定しない型)を扱うローレベルコードで意味を持つ変更だ。以下の3関数が安定化された。
Sized な型のサイズ・アラインメント取得は従来から安全に行えたが、non-Sized 型の生ポインタに対して同等の情報を得るための安全要件がこのリリースで整理・確定した。カスタムアロケータ実装やコレクションの内部実装など、メモリ管理を直接扱うコードにとって重要な安定化だ。
Box::leak の「ラウンドトリップ解放」パターンに注意喚起
言語仕様の変更ではないが、Box::leak のドキュメントに重要な注記が追加された。
Box::leak でメモリをリークさせた後、そのポインタから Box を再構築して解放する「ラウンドトリップ解放」パターンは、現在および将来のコンパイラ最適化と相性が悪く、カスタムアロケータのstable化に向けても問題があるとして、使用を避けるよう推奨されるようになった。代わりに、今回のリリースで同時にstable化された Box::into_non_null の使用が推奨される。この指針は標準ライブラリ内の他の leak 関数にも適用される。
既存コードでこのパターンを使っている場合は早めの移行が望ましい。
そのほかのstable化API
今回のリリースで安定化された主なAPIは以下の通りだ。
IntoIterator for Box<[T; N]>(&/&mut含む)― 固定長配列を格納したBoxを直接イテレートできるVecDeque::retain_back― 末尾側からのretain操作Vec::into_parts/Vec::from_parts―Vecを生ポインタ・長さ・容量に分解/再構築String::from_utf8_lossy_owned― 所有権付きバイト列からのロッシー変換std::fs::set_times/set_times_nofollow― ファイルのタイムスタンプをRustから直接設定できる
アップデートは rustup update stable で取得できる。Cargo・Clippyの変更内容はGitHubのリリースページに詳細がある。
詳細はAnnouncing Rust 1.99.0を参照していただきたい。