Skip to content

Stabilize if let guards (feature(if_let_guard))#141295

Open
Kivooeo wants to merge 1 commit intorust-lang:mainfrom
Kivooeo:if-let-guard-stable
Open

Stabilize if let guards (feature(if_let_guard))#141295
Kivooeo wants to merge 1 commit intorust-lang:mainfrom
Kivooeo:if-let-guard-stable

Conversation

@Kivooeo
Copy link
Member

@Kivooeo Kivooeo commented May 20, 2025

Summary

This proposes the stabilization of if let guards (tracking issue: #51114, RFC: rust-lang/rfcs#2294). This feature allows if let expressions to be used directly within match arm guards, enabling conditional pattern matching within guard clauses.

What is being stabilized

The ability to use if let expressions within match arm guards.

Example:

enum Command {
    Run(String),
    Stop,
    Pause,
}

fn process_command(cmd: Command, state: &mut String) {
    match cmd {
        Command::Run(name) if let Some(first_char) = name.chars().next() && first_char.is_ascii_alphabetic() => {
            // Both `name` and `first_char` are available here
            println!("Running command: {} (starts with '{}')", name, first_char);
            state.push_str(&format!("Running {}", name));
        }
        Command::Run(name) => {
            println!("Cannot run command '{}'. Invalid name.", name);
        }
        Command::Stop if state.contains("running") => {
            println!("Stopping current process.");
            state.clear();
        }
        _ => {
            println!("Unhandled command or state.");
        }
    }
}

Motivation

The primary motivation for if let guards is to reduce nesting and improve readability when conditional logic depends on pattern matching. Without this feature, such logic requires nested if let statements within match arms:

// Without if let guards
match value {
    Some(x) => {
        if let Ok(y) = compute(x) {
            // Both `x` and `y` are available here
            println!("{}, {}", x, y);
        }
    }
    _ => {}
}

// With if let guards
match value {
    Some(x) if let Ok(y) = compute(x) => {
        // Both `x` and `y` are available here
        println!("{}, {}", x, y);
    }
    _ => {}
}

Implementation and Testing

The feature has been implemented and tested comprehensively across different scenarios:

Core Functionality Tests

Scoping and variable binding:

  • scope.rs - Verifies that bindings created in if let guards are properly scoped and available in match arms
  • shadowing.rs - Tests that variable shadowing works correctly within guards
  • scoping-consistency.rs - Ensures temporaries in guards remain valid for the duration of their match arms

Type system integration:

  • type-inference.rs - Confirms type inference works correctly in if let guards
  • typeck.rs - Verifies type mismatches are caught appropriately

Pattern matching semantics:

Error Handling and Diagnostics

  • warns.rs - Tests warnings for irrefutable patterns and unreachable code in guards
  • parens.rs - Ensures parentheses around let expressions are properly rejected
  • macro-expanded.rs - Verifies macro expansions that produce invalid constructs are caught
  • guard-mutability-2.rs - Tests mutability and ownership violations in guards
  • ast-validate-guards.rs - Validates AST-level syntax restrictions

Drop Order and Temporaries

Key insight: Unlike let_chains in regular if expressions, if let guards do not have drop order inconsistencies because:

  1. Match guards are clearly scoped to their arms
  2. There is no "else block" equivalent that could cause temporal confusion

Edition Compatibility

This feature stabilizes on all editions, unlike let chains which was limited to edition 2024. This is safe because:

  1. if let guards don't suffer from the drop order issues that affected let chains in regular if expressions
  2. The scoping is unambiguous - guards are clearly tied to their match arms
  3. Extensive testing confirms identical behavior across all editions

Interactions with Future Features

The lang team has reviewed potential interactions with planned "guard patterns" and determined that stabilizing if let guards now does not create obstacles for future work. The scoping and evaluation semantics established here align with what guard patterns will need.

Unresolved Issues


Related:

@rustbot
Copy link
Collaborator

rustbot commented May 20, 2025

r? @SparrowLii

rustbot has assigned @SparrowLii.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 20, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 20, 2025

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

@Kivooeo Kivooeo force-pushed the if-let-guard-stable branch from 6fe74d9 to 5ee8970 Compare May 20, 2025 17:08
@rustbot
Copy link
Collaborator

rustbot commented May 20, 2025

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@Kivooeo Kivooeo force-pushed the if-let-guard-stable branch from eb0e4b4 to 0358002 Compare May 20, 2025 17:13
@rust-log-analyzer

This comment has been minimized.

@Kivooeo Kivooeo force-pushed the if-let-guard-stable branch from 92a5204 to ab138ce Compare May 20, 2025 17:35
@rust-log-analyzer

This comment has been minimized.

@Kivooeo Kivooeo force-pushed the if-let-guard-stable branch from 5ceca48 to a20c4f6 Compare May 20, 2025 17:57
@rust-log-analyzer

This comment has been minimized.

@Kivooeo Kivooeo force-pushed the if-let-guard-stable branch 2 times, most recently from 1dd9974 to 5796073 Compare May 20, 2025 18:56
@traviscross traviscross added T-lang Relevant to the language team needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 20, 2025
@traviscross
Copy link
Contributor

cc @est31 @ehuss

@traviscross
Copy link
Contributor

cc @Nadrieril

@SparrowLii
Copy link
Member

SparrowLii commented May 21, 2025

This needs a fcp so I'd like to roll this to someone more familiar with this feature
r? compiler

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label May 21, 2025
@rustbot rustbot assigned oli-obk and unassigned SparrowLii May 21, 2025
@traviscross traviscross added I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels May 21, 2025
@oli-obk
Copy link
Contributor

oli-obk commented May 21, 2025

r? @est31

@traviscross
Copy link
Contributor

Cc @rust-lang/fls on an upcoming stabilization.

@theemathas
Copy link
Contributor

theemathas commented Feb 7, 2026

Can the matches!() and assert_matches!() macros be modified to support if let guards?

@ehuss
Copy link
Contributor

ehuss commented Feb 10, 2026

I believe the FCP for this is now complete. I suspect rfcbot missed the notification when there were the recent major GitHub outages.

@Kivooeo Are you ready to rebase?

@traviscross traviscross added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Feb 11, 2026
@traviscross
Copy link
Contributor

Confirmed. The lang FCP is complete. The Reference PR is approved. This is good to go as far as lang is concerned.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from dab1a5e to adac560 Compare February 12, 2026 11:39
@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch 2 times, most recently from 35cc25c to c776f09 Compare February 12, 2026 12:03
@rust-log-analyzer

This comment has been minimized.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from c776f09 to f852026 Compare February 12, 2026 13:04
@rust-log-analyzer

This comment has been minimized.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from f852026 to 32e37c6 Compare February 12, 2026 13:52
@rust-log-analyzer

This comment has been minimized.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from 32e37c6 to dd04e29 Compare February 12, 2026 16:11
@Kivooeo
Copy link
Member Author

Kivooeo commented Feb 12, 2026

Does this need to be reviewed, or can we do r=est31, fee1-dead?

@jackh726
Copy link
Member

Would probably be good to get explicit sign-off by at least one of @est31 or @fee1-dead, since the underlying temporary behavior has changed since their initial r+.

@theemathas theemathas added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 13, 2026
@rust-bors

This comment has been minimized.

@est31
Copy link
Member

est31 commented Feb 14, 2026

I think I'll have time tomorrow in the evening. Exciting that this is ready now!

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from dd04e29 to a68ecfd Compare February 15, 2026 09:25
@rustbot
Copy link
Collaborator

rustbot commented Feb 15, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@est31
Copy link
Member

est31 commented Feb 15, 2026

Today I had the time to give this a closer look/load the problem space back into memory, and re-read this thread as well as the code changes.

The implementation looks good to me. Since my earlier positive review, the stabilization has been held back by two issues: the reference PR as well as drop order concerns raised by @dianne .

Since then, the reference PR has been approved (I found a minor issue in it but it's easy to fix), and @dianne 's concerns have been resolved.

Giving the tests another look and what is being tested, there is some minor things that could get tests:

  • There is two outstanding tests mentioned here but I'd agree they are not blockers. Would be useful to have them though!

  • I had a look at the if let chains edition 2024 gating I added in Stabilize let chains in the 2024 edition #132833 (for let chains in normal if's and while, not match guards). I couldn't find an instance where it's tested that the edition gating of if let chains survives being nested into if let guards. I.e. it's a common mistake to make to track such thing via a global bool variable, instead of having something stack-like that gets opened/destroyed if a new construct is entered/left. The current behavior of the compiler is fine, but it would be nice to have a test to ensure that code like errors on editions<2024:

    let foo = Some(123);
    match foo {
      Some(x) if let v = (if let Some(0) = None && true {} else {}) && let Some(3u32) = None => {}
      _ => {}
    }
  • Lastly, I wonder if warns.rs could be extended to test for irrefutable_let_patterns, especially trailing irrefutable patterns i.e. let x = 0.

r=me with the latter two concerns addressed.

@rust-cloud-vms rust-cloud-vms bot force-pushed the if-let-guard-stable branch from a68ecfd to 964b63f Compare February 16, 2026 12:24
@Kivooeo
Copy link
Member Author

Kivooeo commented Feb 16, 2026

  • Lastly, I wonder if warns.rs could be extended to test for irrefutable_let_patterns, especially trailing irrefutable patterns i.e. let x = 0

could you take a look on wanrs.rs, it's a bit of unclear to me on "trailing irrefutable patterns" part, i added two new cases, but yet not sure if that's what you originally meant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. A-meta Area: Issues & PRs about the rust-lang/rust repository itself A-rustc-dev-guide Area: rustc-dev-guide A-testsuite Area: The testsuite used to check the correctness of rustc A-tidy Area: The tidy tool disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-clippy Relevant to the Clippy team. T-infra Relevant to the infrastructure team, which will review and decide on the PR/issue. T-lang Relevant to the language team WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.