Rust 1.45では、未定義動作の原因になる可能性があると以前から指摘されていた、浮動小数点のキャスト問題に対する修正と、人気のWebフレームワークであるRocketで使用される機能の安定化が行われている。
Rustの価値提案(Rust value proposition)について知っているならば、浮動小数点数を整数にキャストする場合に未定義動作を発生させる既知の問題があったことに驚くだろう。具体的には、以下のコードスニペットはエラーなしでコンパイル可能だが、浮動小数点値300から、0から255までの整数のみ表現可能な8bit符号なし整数へのcast(as
)が原因で、Rust 1.44では動作が未定義になる。
fn cast(x: f32) -> u8 {
x as u8
}
fn main() {
let f = 300.0;
let x = cast(f);
println!("x: {}", x);
}
内部的には、この問題は、上記のケースにおいて"poison"値を生成する、LLVMのfptoi
インストラクションが関連している。Rustにはunsafe
というキーワードがあって、Rustの保証を停止したいコードブロックをマークできる、ということを思い出してほしい。上記のスニペットには、unsafeとマークされていないにも関わらずunsafeなコードが含まれていて、Rustが安全な言語であるという保証が破られているのだ。
Rustチームがこの不適切なcast問題の修正に何年も要したのは、これをどのように扱うべきかが不明確であったというのが大きな理由だ。最終的には、as
を"飽和的(saturating)"castを行う安全な演算子とすることに決定された。すなわち、過大な浮動小数点数は整数で表現される最大値に、小さすぎる浮動小数点数やNaNは0に、それぞれcastされる。さらにチームは、Rustの安全な動作をスキップしたい場合に使用する、unsafe
castも新たに用意した。
let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };
飽和型castはオーバーフローを安全に扱う方法を提供可能な反面、数学的な観点からは不正確な結果を導き出す可能性がある。この点から、as
はRustの値変換では慣用的表現と見なされておらず、RustのClipplyリンタでフラグ付けされる。Rustで浮動小数点数を整数に変換する場合の、より慣用的な方法は、失敗しない(non-fallible)castであればinto
を、失敗する可能性のある(fallable)castではtry_into
を、それぞれ使うことだ。
Rust 1.45ではさらに、手続きマクロ(procedural macro)を起動する場所として、"式の一部として"、"パターンマッチ内で"、"ステートメントとして"の3つのサポートが加えられた。手続きマクロはRust 1.30で拡張されており、関数風マクロ、つまり関数のように見えるマクロの定義が可能になっている。例えば以下のスニペットでは、SQLステートメントの解析を要求するために必要なRust構文木を生成するマクロを定義している。
// parse an SQL statement
let sql = sql!(SELECT * FROM posts WHERE id=1);
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
...
}
この変更の重要性は、人気の高い宣言型WebフレームワークであるRocket内部での使用に関わっている。このフレームワークでは、Rustの夜間ビルドでのみ使用可能な実験的機能がいくつか使用されているのだ。Rocketの人気が高いため、Rustチームはその安定化に向けて開発を進めているが、一方のRocket側でも他の削除を行っている。この活動の成果として、まだリリースされていないRocket 0.5は、安定版Rustでコンパイル可能な初のバージョンになる予定である。
ここで挙げたものの他にも、Rust 1.45には多くの安定化や修正が含まれている。詳細は公式リリースノートを参照してほしい。