Skip to content

← Shape Your Data: Structs, Enums, Pattern Matching step 16 of 26

Medium Primitives

Let-chains: the edition-2024 feature that deletes your nesting

Walk two lists in parallel. At each index where both lists have an element and the pair’s sum is at most limit, that sum is a candidate. Return the largest candidate, or None if there is none.

pub fn best_pair(a: Vec<i64>, b: Vec<i64>, limit: i64) -> Option<i64>

With a = [1,2,3], b = [10,20,30], limit = 25: the sums are 11, 22 and 33; the third is over the limit; the answer is 22.

The wart, and its removal

Three conditions, three levels of indentation:

if let Some(x) = a.get(i) {
    if let Some(y) = b.get(i) {
        if x + y <= limit {
            // finally
        }
    }
}

Rust 2024 lets you chain them with &&:

if let Some(x) = a.get(i)
    && let Some(y) = b.get(i)
    && x + y <= limit
{
    // one level of indentation
}

Bindings from earlier links are in scope in later ones — x is available to the x + y <= limit test. You can mix let links and ordinary boolean links freely, and it works in while too.

The starter is the nested version, and collapsible_if (on by default) will reject it. Note what that means: clippy is enforcing an edition-2024 feature as the house style. That is a strong signal about which form the language considers correct now.

What an edition actually is, demonstrated

This is the useful part. Let-chains are not available in Rust 2021. The identical file compiles under --edition 2024 and fails under --edition 2021 with:

error[E0658]: let chains are only allowed in Rust 2024 or later

Editions are usually explained as “opt-in breaking changes”, which sounds like bureaucracy. Here is a case where the machinery is doing real work. Let-chains depend on a change Rust 2024 made to the temporary scopes of if let: in 2021, temporaries created by the scrutinee live until the end of the whole if/else; in 2024 they are dropped at the end of the if let‘s own block. That is a change in drop order — observable, and capable of breaking working code, which is why it needed an edition boundary and why rustc ships an if_let_rescope lint to help people migrate.

Without the new scoping, a chained if let ... && let ... would have ill-defined drop behaviour for the intermediate temporaries. The feature and the edition are genuinely coupled.

Practical consequence for you: almost every tutorial, Stack Overflow answer and LLM output you find on this topic is stale. This harness compiles with --edition 2024, so let-chains work here.

Two limits worth knowing

There is no || version. if let Some(x) = a || let Some(x) = b does not exist — with alternation the compiler could not say which bindings are live in the body. Use an or-pattern inside a single let if the shapes allow it.

A binding is not in scope in the else branch. If the chain failed, some of the lets may not have matched, so none of their bindings are available in else.

Related lints

unnecessary_unwrap (on by default) catches if x.is_some() { x.unwrap() } — a shape a let-chain replaces entirely. nonminimal_bool simplifies redundant boolean algebra. option_if_let_else (allow-by-default, nursery) suggests map_or for some of these; treat it as a suggestion, not a rule.

Remember the grade is compile + tests + clippy -D warnings.