Series: The Rust Annals

Vol. I Issue 21 nlopes.dev

Announcing Rust 1.20

Associated constants add a substantial language capability alongside broad standard-library, security, and Cargo improvements.

Rust 1.20 adds associated constants, allowing constants to belong to types, traits, and enums and to participate in constant expressions. The release also introduces potentially faster unstable slice sorting, ships Stack Clash mitigations, moves Cargo credentials into a more restrictive file, and improves the layout of packages containing substantial secondary binaries.

Associated constants bring constant values to types and traits

Earlier Rust releases supported associated functions on traits, structs, and enums. Rust 1.20 extends associated items with constants, as proposed in RFC 195. A constant can now be attached directly to a type:

struct Struct;

impl Struct {
    const ID: u32 = 0;
}

fn main() {
    println!("the ID of Struct is: {}", Struct::ID);
}

A trait may declare an associated constant without providing its value, leaving each implementation to define it:

trait Trait {
    const ID: u32;
}

struct Struct;

impl Trait for Struct {
    const ID: u32 = 5;
}

fn main() {
    println!("{}", Struct::ID);
}

Previously, a trait representing floating-point numbers might expose constant values through functions:

trait Float {
    fn nan() -> Self;
    fn infinity() -> Self;
    ...
}

Because these were functions, they could not be used in constant expressions even though they returned constants. A design therefore also needed separate constants and forwarding methods:

mod f32 {
    const NAN: f32 = 0.0f32 / 0.0f32;
    const INFINITY: f32 = 1.0f32 / 0.0f32;

    impl Float for f32 {
        fn nan() -> Self {
            f32::NAN
        }
        fn infinity() -> Self {
            f32::INFINITY
        }
    }
}

Associated constants let the trait express those values directly:

trait Float {
    const NAN: Self;
    const INFINITY: Self;
    ...
}
mod f32 {
    impl Float for f32 {
        const NAN: f32 = 0.0f32 / 0.0f32;
        const INFINITY: f32 = 1.0f32 / 0.0f32;
    }
}

The feature arrived almost three years after RFC 195 proposed associated items. The release post also notes ongoing internal work on constant evaluation intended to increase Rust's compile-time metaprogramming capabilities.

The standard library adds unstable sorting and security mitigations

Three new slice methods provide unstable sorting: slice::sort_unstable_by_key, slice::sort_unstable_by, and slice::sort_unstable. Here, “unstable” describes whether equal elements retain their original relative order. Given:

rust
crate
package
cargo

a stable sort using only the first letter must produce:

crate
cargo
package
rust

An unstable sort may instead produce:

cargo
crate
package
rust

The reduced constraint may make these methods faster than stable sorting when relative order does not matter; the announcement advises checking both. The design and benchmarks are in RFC 1884.

Rust 1.20 also ships Stack Clash mitigations, notably stack probes and skipping the main thread's manual stack guard on Linux. Users need take no action beyond using Rust 1.20 to receive these protections.

Other library changes include unimplemented! accepting a message explaining why something is not yet implemented, an upgrade to Unicode 10.0.0, and Rust implementations of floating-point min and max that no longer rely on cmath.

The remaining stabilized APIs are:

Cargo protects registry credentials and separates larger binaries

Cargo previously stored the crates.io authentication token in ~/.cargo/config. As a configuration file, it was often created with 644 permissions and was therefore world-readable despite containing a secret token. Rust 1.20 moves the token to ~/.cargo/credentials, where it can use 600 permissions and be hidden from other users on the system.

Cargo also conventionally supports directory-based secondary binaries. Packages can use src/bin/client/main.rs and src/bin/server/main.rs instead of putting client.rs, server.rs, and both binaries' submodules together in src/bin. This lets larger secondary binaries keep their logic separate.

Documentation tests resolve include! paths from the current file

A documentation-test bug was fixed: relative paths passed to include! had erroneously been resolved relative to the working directory. They are now resolved relative to the current file.

Reproduced from the Rust blog under its publication licence. Typeset in Literata.