Stabilize `if let` guards (`feature(if_let_guard)`) ## Summary This proposes the stabilization of `if let` guards (tracking issue: rust-lang/rust#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: ```rust 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: ```rust // 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`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/scope.rs) - Verifies that bindings created in `if let` guards are properly scoped and available in match arms - [`shadowing.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs) - Tests that variable shadowing works correctly within guards - [`scoping-consistency.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/scoping-consistency.rs) - Ensures temporaries in guards remain valid for the duration of their match arms **Type system integration:** - [`type-inference.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs) - Confirms type inference works correctly in `if let` guards - [`typeck.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs) - Verifies type mismatches are caught appropriately **Pattern matching semantics:** - [`exhaustive.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/exhaustive.rs) - Validates that `if let` guards are correctly handled in exhaustiveness analysis - [`move-guard-if-let.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs) and [`move-guard-if-let-chain.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let-chain.rs) - Test that conditional moves in guards are tracked correctly by the borrow checker ### Error Handling and Diagnostics - [`warns.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs) - Tests warnings for irrefutable patterns and unreachable code in guards - [`parens.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs) - Ensures parentheses around `let` expressions are properly rejected - [`macro-expanded.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs) - Verifies macro expansions that produce invalid constructs are caught - [`guard-mutability-2.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/guard-mutability-2.rs) - Tests mutability and ownership violations in guards - [`ast-validate-guards.rs`](5796073c13/tests/ui/rfcs/rfc-2497-if-let-chains/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 - [`drop-order.rs`](5796073c13/tests/ui/rfcs/rfc-2294-if-let-guard/drop-order.rs) - Check drop order of temporaries create in match guards - [`compare-drop-order.rs`](aef3f5fdf0/tests/ui/rfcs/rfc-2294-if-let-guard/compare-drop-order.rs) - Compares drop order between `if let` guards and nested `if let` in match arms, confirming they behave identically across all editions - rust-lang/rust#140981 - A complicated drop order test involved `let chain` was made by @est31 - [`drop-order-comparisons-let-chains.rs`](902b4d2878/tests/ui/drop/drop-order-comparisons-let-chains.rs) - Compares drop order between `let chains` in `if let guard` and regular `if` expressions - [`if-let-guards.rs`](5650d716e0/tests/ui/drop/if-let-guards.rs) - Test correctness of drop order for bindings and temporaries - [`if-let-guards-2`](3a6c8c8f3d/tests/ui/drop/if-let-guards-2.rs) - The same test as above but more comprehensive and tests more interactions between different features and their drop order, checking that drop order is correct, created by @traviscross ## 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 - [x] - rust-lang/rust#140981 - [x] - added tests description by @jieyouxu request - [x] - Concers from @scottmcm about stabilizing this across all editions - [x] - check if drop order in all edition when using `let chains` inside `if let` guard is the same - [x] - interactions with guard patters - [x] - pattern bindings drops before guard bindings https://github.com/rust-lang/rust/pull/143376 - [x] - documentaion (https://github.com/rust-lang/reference/pull/1957) - [ ] (non-blocking) add tests for [this](https://github.com/rust-lang/rust/issues/145237) and [this](https://github.com/rust-lang/rust/pull/141295#issuecomment-3173059821) --- **Related:** - Tracking Issue: rust-lang/rust#51114 - RFC: rust-lang/rfcs#2294 - Documentation PR: https://github.com/rust-lang/reference/pull/1957
rust-analyzer is a language server that provides IDE functionality for writing Rust programs. You can use it with any editor that supports the Language Server Protocol (VS Code, Vim, Emacs, Zed, etc).
rust-analyzer features include go-to-definition, find-all-references, refactorings and code completion. rust-analyzer also supports integrated formatting (with rustfmt) and integrated diagnostics (with rustc and clippy).
Internally, rust-analyzer is structured as a set of libraries for analyzing Rust code. See Architecture in the manual.
Quick Start
https://rust-analyzer.github.io/book/installation.html
Documentation
If you want to contribute to rust-analyzer check out the CONTRIBUTING.md or if you are just curious about how things work under the hood, see the Contributing section of the manual.
If you want to use rust-analyzer's language server with your editor of choice, check the manual. It also contains some tips & tricks to help you be more productive when using rust-analyzer.
Security and Privacy
See the security and privacy sections of the manual.
Communication
For usage and troubleshooting requests, please use "IDEs and Editors" category of the Rust forum:
https://users.rust-lang.org/c/ide/14
For questions about development and implementation, join rust-analyzer working group on Zulip:
https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
Quick Links
- Website: https://rust-analyzer.github.io/
- Metrics: https://rust-analyzer.github.io/metrics/
- API docs: https://rust-lang.github.io/rust-analyzer/ide/
- Changelog: https://rust-analyzer.github.io/thisweek
License
rust-analyzer is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See LICENSE-APACHE and LICENSE-MIT for details.