Rust 1.45修復了一個長期存在的浮點數強制轉換問題,該問題可能導致未定義行爲(undefined behaviour )異常,並穩定了流行 Web框架Rocket所使用的特性。
將浮點數轉換爲整數時,Rest會拋出一個 未定義行爲(undefined behaviour)的已知異常。如果你對Rust的 value proposition有所瞭解的話,可能會對此感到驚訝。具體來說,下面的代碼片段雖然編譯時不會報錯,但由於使用了 cast ( as
) 將浮點數300強制轉換爲8位無符號整數(僅表示值介於0到255之間的整數),在Rust 1.44中會拋出未定義行爲的異常:
fn cast(x: f32) -> u8 {
x as u8
}
fn main() {
let f = 300.0;
let x = cast(f);
println!("x: {}", x);
}
在底層,這個問題與LLVM的 fptoui
指令有關,該指令在上述情況下使用會生成一個“有毒”的值。回想一下,Rust提供了 unsafe
關鍵字來標記希望 掛起Rust安全保證的代碼塊。上面所示的代碼片段雖然沒有被標記爲不安全,但它卻包含了不安全的代碼,這違背了Rust作爲安全語言的承諾。
Rust團隊花了幾年的時間來修復這個不健全的強制轉換問題,主要是因爲不清楚怎樣才能正確地處理它。最終,他們決定讓 as
執行“saturating”強制轉換,這意味着將過大的浮點數強制轉換爲可表示的最大整數,將過小的浮點數和NaN強制轉換爲0。此外,他們還引入了一種新的 unsafe
強制轉換,如果你想要跳過Rust的安全行爲可以使用如下代碼:
let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };
雖然saturating強制轉換提供了一種處理溢出的安全方法,但從數學角度來看,它仍然會產生錯誤的結果。 這就是爲什麼as
在Rust中不被視爲值間轉換的慣用方法,並且還被Rust的Clipply linter標記的原因。在Rust中,將浮點數轉換爲整數更慣用的方法是,使用 into
來進行不會出錯的強制轉換,而使用 try_into
來進行可能會出錯的強制轉換。
Rust 1.45還在三個新地方增加了對調用過程宏的支持,即:作爲表達式的一部分、在模式匹配中或作爲語句。過程宏在Rust 1.30中進行了擴展,以支持類函數宏的定義(即看起來像函數的宏)。例如,下面的代碼片段定義了一個 sql
宏,該宏可生成解析SQL語句所需的Rust語法樹:
// 解析SQL語句
let sql = sql!(SELECT * FROM posts WHERE id=1);
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
...
}
該變更版本的重要性還和 它在Rocket中的使用有關,Rocket是一個流行的聲明式Web框架,它使用了幾個僅在nightly Rust中可用的實驗特性。由於Rocket非常受歡迎,Rust團隊一直在努力穩定其中的一些特性,而Rocket則移除了其他的一些特性。作爲這一努力的結果,尚未發佈的Rocket 0.5將成爲首個能夠使用穩定的Rust進行編譯的Rocket版本。
除了本文提到的內容之外,Rust 1.45還包含了許多其他特性的穩定和修復。 請不要錯過官方發佈的詳細說明。
原文鏈接:
Rust 1.45 Fixes Cast Unsoundness and Stabilizes Support for Web Framework Rocket