Stabilize if let guards (feature(if_let_guard)) by Kivooeo · Pull Request #141295 · rust-lang/rust (original) (raw)
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 letguards 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 letguards - typeck.rs - Verifies type mismatches are caught appropriately
Pattern matching semantics:
- exhaustive.rs - Validates that
if letguards are correctly handled in exhaustiveness analysis - move-guard-if-let.rs and 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 - Tests warnings for irrefutable patterns and unreachable code in guards
- parens.rs - Ensures parentheses around
letexpressions 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:
- Match guards are clearly scoped to their arms
- There is no "else block" equivalent that could cause temporal confusion
- drop-order.rs - Check drop order of temporaries create in match guards
- compare-drop-order.rs - Compares drop order between
if letguards and nestedif letin match arms, confirming they behave identically across all editions - Add match guard let chain drop order and scoping tests #140981 - A complicated drop order test involved
let chainwas made by @est31 - drop-order-comparisons-let-chains.rs - Compares drop order between
let chainsinif let guardand regularifexpressions - if-let-guards.rs - Test correctness of drop order for bindings and temporaries
- if-let-guards-2 - 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:
if letguards don't suffer from the drop order issues that affectedlet chainsin regularifexpressions- The scoping is unambiguous - guards are clearly tied to their match arms
- 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
- - Add match guard let chain drop order and scoping tests #140981
- - added tests description by @jieyouxu request
- - Concers from @scottmcm about stabilizing this across all editions
- - check if drop order in all edition when using
let chainsinsideif letguard is the same - - interactions with guard patters
- - pattern bindings drops before guard bindings add a scope for if let guard temporaries and bindings #143376
- - documentaion (specify if let guards with updated scoping rules reference#1957)
- (non-blocking) add tests for this and this
Related:
- Tracking Issue: Tracking issue for RFC 2294, "if let guard" #51114
- RFC: propose if let guard rfcs#2294
- Documentation PR: specify if let guards with updated scoping rules reference#1957