Skip to content

let and if let have different closure capture behaviors #153982

@theemathas

Description

@theemathas

I tried this code:

#![expect(dead_code, irrefutable_let_patterns)]

struct Thing(String, String);

fn foo(x: Thing) {
    let closure = || {
        if let Thing(_a, _) = x {}
    };
    println!("{}", size_of_val(&closure));
}

fn bar(x: Thing) {
    let closure = || {
        match x {
            Thing(_a, _) => {}
        }
    };
    println!("{}", size_of_val(&closure));
}

fn baz(x: Thing) {
    let closure = || {
        let Thing(_a, _) = x;
    };
    println!("{}", size_of_val(&closure));
}

fn main() {
    foo(Thing(String::from("a"), String::from("b")));
    bar(Thing(String::from("a"), String::from("b")));
    baz(Thing(String::from("a"), String::from("b")));
}

I expected the three printed numbers to be the same. Instead, in editions 2021 and 2024, the code prints 48 then 24 then 24.

It seems that if let causes the entire value of x to be captured, but let and match only capture the relevant field. This seems inconsistent.

cc @Nadrieril @meithecatte

Meta

Reproducible on the playground with version 1.96.0-nightly (2026-03-12 3102493c71626b5912d1) and also on version 1.94.0.

However, testing on Godbolt with version 1.93.0 (which is before #138961), it prints 48 then 48 then 24.

Metadata

Metadata

Labels

A-closuresArea: Closures (`|…| { … }`)A-patternsRelating to patterns and pattern matchingC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions