Series: The Rust Annals

Vol. I Issue 39 nlopes.dev

Announcing Rust 1.38.0

Rust 1.38.0’s highlight is pipelined compilation: Cargo can begin building dependent crates as soon as their metadata is ready, improving some optimized clean builds without code changes. The release also adds a targeted lint for definitely incorrect uses of uninitialized or zeroed memory, permits deprecation notices on macros, and expands debugging and numeric APIs in the standard library.

Cargo pipelines crate compilation from dependency metadata

The compiler does not need a dependency to be fully built before compiling a dependent crate; it needs the dependency’s metadata, including its types, dependencies, and exports. Because that metadata is produced early, Cargo now automatically starts building dependent crates as soon as it is ready.

This has no effect on single-crate builds. Testing produced reports of 10–20% compilation-speed increases for optimized, clean builds of some crate graphs, while other graphs improved little. Results depend on the build hardware, and no code changes are needed.

A recursive lint catches some definitely invalid initialization

std::mem::uninitialized is essentially impossible to use safely, and MaybeUninit<T> should be used instead. Rust 1.38.0 does not yet deprecate mem::uninitialized—that is planned for a future release—but rustc now warns about a narrow class of incorrect initializations performed with mem::uninitialized or mem::zeroed.

Types such as &T and Box<T> cannot contain an all-zero bit pattern because these pointer-like objects cannot be null. Initializing them that way is undefined behavior. The lint recursively examines larger structs, so it warns for this nested case:

struct Wrap<T>(T);
struct Outer(Wrap<Wrap<Wrap<Box<i32>>>>);
struct CannotBeZero {
    outer: Outer,
    foo: i32,
    bar: f32
}

...

let bad_value: CannotBeZero = unsafe { std::mem::uninitialized() };

The check does not yet lint initialization of NonNull<T> or NonZero<T>, and it does not cover every unsound use of either function. It only identifies code known to be wrong; all code should still be moved to MaybeUninit.

Macros gain deprecation notices and generic code gains type-name diagnostics

The #[deprecated] attribute, introduced in Rust 1.9.0 to notify users that a crate item is deprecated and will be removed in a future release, can now also be applied to macros.

For debugging generic code, std::any::type_name can report at run time the concrete types used to instantiate type parameters:

fn gen_value<T: Default>() -> T {
    println!("Initializing an instance of {}", std::any::type_name::<T>());
    Default::default()
}

fn main() {
    let _: i32 = gen_value();
    let _: String = gen_value();
}

This prints:

Initializing an instance of i32
Initializing an instance of alloc::string::String

The returned string is only a best-effort description for debugging. Its exact contents and format are not guaranteed, multiple types may share the same value, and that value may change in future compiler releases.

Standard-library implementations and numeric operations expand

The release broadens several existing implementations:

The newly stabilized functions are:

A correction to the original announcement clarifies that Duration::div_duration_f32 and Duration::div_duration_f64 are not yet stable.

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