Rust 1.45 includes a fix for a long-standing float cast issue potentially causing undefined behaviour and stabilizes features used by popular Web framework Rocket.
If you know something about Rust value proposition, it may strike you as surprising that Rust included a known cause for undefined behaviour when casting a floating point number to an integer. Specifically, the following code snippet, although compiled without errors, has undefined behaviour in Rust 1.44 due to the cast (as
) of floating point value 300 to an 8 bit unsigned integer, which can only represent integer values comprised between 0 and 255:
fn cast(x: f32) -> u8 {
x as u8
}
fn main() {
let f = 300.0;
let x = cast(f);
println!("x: {}", x);
}
Under the hood, the issue is related to LLVM's fptoui
instruction, which generates a "poison" value when used in the above case. Recall that Rust provides the unsafe
keyword to mark code blocks where you want to suspend Rust guarantees. The snippet shown above contains unsafe code in spite of not being marked unsafe, which kind of breaks Rust promise of being a safe language.
It took several years for the Rust team to fix this unsound cast issue, mostly because it was not clear how to correctly deal with it. Eventually, they decided to make as
a safe operator performing a "saturating" cast, which means too-large floats are cast to the maximum representable integer, too-small floats and NaN are cast to 0. Additionally, they introduced a new unsafe
cast that you can use when you want to skip Rust safe behaviour:
let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };
Although saturating cast provides a safe way to deal with overflow, it may still produce incorrect results from a mathematical point of view. This is the reason why as
is not considered idiomatic in Rust to convert between values and is flagged by Rust's Clipply linter. More idiomatic ways to convert from float to int in Rust would then be using into
for non-fallible casts and try_into
for fallible casts.
Rust 1.45 also add supports for invoking procedural macros in three new places, i.e., as part of an expression, within a pattern match, or as a statement. Procedural macros were extended in Rust 1.30 to enable the definition of function-like macros, that is, macros that look like functions. For example, the following snippet defines an sql
macros that generates the Rust syntax tree required to parse an SQL statement:
// parse an SQL statement
let sql = sql!(SELECT * FROM posts WHERE id=1);
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
...
}
The importance of this change is related to its use within Rocket, a popular declarative web framework that used several experimental features only available in nightly Rust. Given Rocket popularity, the Rust team has worked hard to stabilize some of them, while Rocket has removed others. As a result of this effort, not-yet released Rocket 0.5 will be the first Rocket version able to compile with stable Rust.
Besides what mentioned here, Rust 1.45 includes many additional stabilizations and fixes. Do not miss the official release notes for full detail.