Announcing Rust 1.45.0

July 16, 2020 · The Rust Release Team

The Rust team is happy to announce a new version of Rust, 1.45.0. Rust is a programming language that is empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, getting Rust 1.45.0 is as easy as:

rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.45.0 on GitHub.

What's in 1.45.0 stable

There are two big changes to be aware of in Rust 1.45.0: a fix for some long-standing unsoundness when casting between integers and floats, and the stabilization of the final feature needed for one of the more popular web frameworks to work on stable Rust.

Fixing unsoundness in casts

Issue 10184 was originally opened back in October of 2013, a year and a half before Rust 1.0. As you may know, rustc uses LLVM as a compiler backend. When you write code like this:

pub fn cast(x: f32) -> u8 {
    x as u8
}

The Rust compiler in Rust 1.44.0 and before would produce LLVM-IR that looks like this:

define i8 @_ZN10playground4cast17h1bdf307357423fcfE(float %x) unnamed_addr #0 {
start:
  %0 = fptoui float %x to i8
  ret i8 %0
}

That fptoui implements the cast, it is short for "floating point to unsigned integer."

But there's a problem here. From the docs:

The ‘fptoui’ instruction converts its floating-point operand into the nearest (rounding towards zero) unsigned integer value. If the value cannot fit in ty2, the result is a poison value.

Now, unless you happen to dig into the depths of compilers regularly, you may not understand what that means. It's full of jargon, but there's a simpler explanation: if you cast a floating point number that's large to an integer that's small, you get undefined behavior.

That means that this, for example, was not well-defined:

fn cast(x: f32) -> u8 {
    x as u8
}

fn main() {
    let f = 300.0;

    let x = cast(f);

    println!("x: {}", x);
}

On Rust 1.44.0, this happens to print "x: 0" on my machine. But it could print anything, or do anything: this is undefined behavior. But the unsafe keyword is not used within this block of code. This is what we call a "soundness" bug, that is, it is a bug where the compiler does the wrong thing. We tag these bugs as I-unsound on our issue tracker, and take them very seriously.

This bug took a long time to resolve, though. The reason is that it was very unclear what the correct path forward was.

In the end, the decision was made to do this:

  • as would perform a "saturating cast".
  • A new unsafe cast would be added if you wanted to skip the checks.

This is very similar to array access, for example:

  • array[i] will check to make sure that array has at least i + 1 elements.
  • You can use unsafe { array.get_unchecked(i) } to skip the check.

So, what's a saturating cast? Let's look at a slightly modified example:

fn cast(x: f32) -> u8 {
    x as u8
}

fn main() {
    let too_big = 300.0;
    let too_small = -100.0;
    let nan = f32::NAN;

    println!("too_big_casted = {}", cast(too_big));
    println!("too_small_casted = {}", cast(too_small));
    println!("not_a_number_casted = {}", cast(nan));
}

This will print:

too_big_casted = 255
too_small_casted = 0
not_a_number_casted = 0

That is, numbers that are too big turn into the largest possible value. Numbers that are too small produce the smallest possible value (which is zero). NaN produces zero.

The new API to cast in an unsafe manner is:

let x: f32 = 1.0;
let y: u8 = unsafe { x.to_int_unchecked() };

But as always, you should only use this method as a last resort. Just like with array access, the compiler can often optimize the checks away, making the safe and unsafe versions equivalent when the compiler can prove it.

Stabilizing function-like procedural macros in expressions, patterns, and statements

In Rust 1.30.0, we stabilized "function-like procedural macros in item position." For example, the gnome-class crate:

Gnome-class is a procedural macro for Rust. Within the macro, we define a mini-language which looks as Rust-y as possible, and that has extensions to let you define GObject subclasses, their properties, signals, interface implementations, and the rest of GObject's features. The goal is to require no unsafe code on your part.

This looks like this:

gobject_gen! {
    class MyClass: GObject {
        foo: Cell<i32>,
        bar: RefCell<String>,
    }

    impl MyClass {
        virtual fn my_virtual_method(&self, x: i32) {
            ... do something with x ...
        }
    }
}

The "in item position" bit is some jargon, but basically what this means is that you could only invoke gobject_gen! in certain places in your code.

Rust 1.45.0 adds the ability to invoke procedural macros in three new places:

// imagine we have a procedural macro named "mac"

mac!(); // item position, this was what was stable before

// but these three are new:
fn main() {
  let expr = mac!(); // expression position

  match expr {
      mac!() => {} // pattern position
  }

  mac!(); // statement position
}

Being able to use macros in more places is interesting, but there's another reason why many Rustaceans have been waiting for this feature for a long time: Rocket. Initially released in December of 2016, Rocket is a popular web framework for Rust often described as one of the best things the Rust ecosystem has to offer. Here's the "hello world" example from its upcoming release:

#[macro_use] extern crate rocket;

#[get("/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
    format!("Hello, {} year old named {}!", age, name)
}

#[launch]
fn rocket() -> rocket::Rocket {
    rocket::ignite().mount("/hello", routes![hello])
}

Until today, Rocket depended on nightly-only features to deliver on its promise of flexibility and ergonomics. In fact, as can be seen on the project's homepage, the same example above in the current version of Rocket requires the proc_macro_hygiene feature to compile. However, as you may guess from the feature's name, today it ships in stable! This issue tracked the history of nightly-only features in Rocket. Now, they're all checked off!

This next version of Rocket is still in the works, but when released, many folks will be very happy :)

Library changes

In Rust 1.45.0, the following APIs were stabilized:

Additionally, you can use char with ranges, to iterate over codepoints:

for ch in 'a'..='z' {
    print!("{}", ch);
}
println!();
// Prints "abcdefghijklmnopqrstuvwxyz"

For a full list of changes, see the full release notes.

Other changes

There are other changes in the Rust 1.45.0 release: check out what changed in Rust, Cargo, and Clippy.

Contributors to 1.45.0

Many people came together to create Rust 1.45.0. We couldn't have done it without all of you. Thanks!