tech, developers, and the code underneath

issue 050· news·

Rust 1.88 ships let chains, and a decade-old papercut closes

if let A = a && let B = b finally compiles. Also naked functions and automatic cargo cache cleaning.

Rust 1.88 stabilizes let chains in the 2024 edition. If you have written Rust for more than a week you have wanted this.

the feature#

You can now chain let bindings with boolean conditions in if and while:

rust
if let Some(user) = session.user()
    && user.is_admin()
    && let Ok(perms) = load_permissions(user.id)
    && perms.contains(Permission::Delete)
{
    delete_record(id)?;
}

Before this you wrote the pyramid:

rust
if let Some(user) = session.user() {
    if user.is_admin() {
        if let Ok(perms) = load_permissions(user.id) {
            if perms.contains(Permission::Delete) {
                delete_record(id)?;
            }
        }
    }
}

Or you restructured with early returns and helper functions, which is often better anyway but was not always applicable — particularly inside a match arm or a loop body where you cannot return.

the edition caveat#

Let chains require the 2024 edition. This is not arbitrary: the tail-expression temporary scope changes in 2024 are what make the drop semantics of a chained let well-defined. In earlier editions the temporaries created in the chain would live too long and the borrow behavior would be surprising in ways that could not be fixed without a breaking change.

This is a nice demonstration of what editions are actually for. A feature was blocked for years on a semantic problem; the edition boundary let them fix the semantics; the feature shipped.

the rest of the release#

Naked functions. #[unsafe(naked)] with a naked_asm! body produces a function with no compiler-generated prologue or epilogue. You need this for interrupt handlers, syscall entry points, and context switching. Previously it required an external crate with fragile assumptions.

Automatic cargo cache cleaning. Cargo now garbage-collects unused files in its global cache. If you have been running cargo clean on a cron job or watching ~/.cargo/registry grow to forty gigabytes, that is handled now.

cfg(true) and cfg(false) as boolean literals in configuration predicates. Small, and it removes a genuinely silly workaround people were using.

Cell::update, several const stabilizations, and the usual batch of API additions.

the pattern in Rust's release cadence#

Rust ships every six weeks, forever, with no marketing cycle and no "major release." What that produces is a language that improves continuously in small increments, where any given release seems minor and the five-year delta is enormous.

Compare Rust in 2020 — no async traits, no GATs, no let chains, no let-else, no const generics worth using, a much worse compiler error story — to Rust today. The difference is large and there was never a moment where it happened.

That is a good way to run a language. It is also why "should I learn Rust" gets different answers from people who last tried it at different times, and why the answer today is different from the answer three years ago.

Dom, June 30, 2025

get README in your inbox

One dispatch, no noise. Tech and developer news, plus the occasional long piece on the craft.

subscribe →