Skip to content

Commit 95b115f

Browse files
Rollup merge of rust-lang#141295 - Kivooeo:if-let-guard-stable, r=est31,fee1-dead
Stabilize `if let` guards (`feature(if_let_guard)`) ## Summary This proposes the stabilization of `if let` guards (tracking issue: rust-lang#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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/shadowing.rs) - Tests that variable shadowing works correctly within guards - [`scoping-consistency.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/type-inference.rs) - Confirms type inference works correctly in `if let` guards - [`typeck.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/typeck.rs) - Verifies type mismatches are caught appropriately **Pattern matching semantics:** - [`exhaustive.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/move-guard-if-let.rs) and [`move-guard-if-let-chain.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/warns.rs) - Tests warnings for irrefutable patterns and unreachable code in guards - [`parens.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/parens.rs) - Ensures parentheses around `let` expressions are properly rejected - [`macro-expanded.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/macro-expanded.rs) - Verifies macro expansions that produce invalid constructs are caught - [`guard-mutability-2.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/guard-mutability-2.rs) - Tests mutability and ownership violations in guards - [`ast-validate-guards.rs`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/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`](https://github.com/rust-lang/rust/blob/5796073c134eaac30475f9a19462c4e716c9119c/tests/ui/rfcs/rfc-2294-if-let-guard/drop-order.rs) - Tests that temporaries in guards are dropped at the correct time - [`compare-drop-order.rs`](https://github.com/rust-lang/rust/blob/aef3f5fdf052fbbc16e174aef5da6d50832ca316/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#140981 - A complicated drop order test involved `let chain` was made by `@est31` ## 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 All blocking issues have been resolved: - [x] - rust-lang#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 --- **Related:** - Tracking Issue: rust-lang#51114 - RFC: rust-lang/rfcs#2294 - Documentation PR: rust-lang/reference#1823
2 parents 544884c + a20a9aa commit 95b115f

File tree

139 files changed

+528
-708
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

139 files changed

+528
-708
lines changed

compiler/rustc_ast/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
// tidy-alphabetical-start
88
#![allow(internal_features)]
9+
#![cfg_attr(bootstrap, feature(if_let_guard))]
910
#![doc(
1011
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
1112
test(attr(deny(warnings)))
@@ -14,7 +15,6 @@
1415
#![feature(array_windows)]
1516
#![feature(associated_type_defaults)]
1617
#![feature(box_patterns)]
17-
#![feature(if_let_guard)]
1818
#![feature(macro_metavar_expr)]
1919
#![feature(negative_impls)]
2020
#![feature(never_type)]

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
3333
// tidy-alphabetical-start
3434
#![allow(internal_features)]
35+
#![cfg_attr(bootstrap, feature(if_let_guard))]
3536
#![doc(rust_logo)]
3637
#![feature(assert_matches)]
3738
#![feature(box_patterns)]
3839
#![feature(exact_size_is_empty)]
39-
#![feature(if_let_guard)]
4040
#![feature(rustdoc_internals)]
4141
// tidy-alphabetical-end
4242

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,11 +454,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
454454
}
455455
};
456456
}
457-
gate_all!(
458-
if_let_guard,
459-
"`if let` guards are experimental",
460-
"you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`"
461-
);
462457
gate_all!(let_chains, "`let` expressions in this position are unstable");
463458
gate_all!(
464459
async_trait_bounds,

compiler/rustc_ast_passes/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
55
// tidy-alphabetical-start
66
#![allow(internal_features)]
7+
#![cfg_attr(bootstrap, feature(if_let_guard))]
78
#![doc(rust_logo)]
89
#![feature(box_patterns)]
9-
#![feature(if_let_guard)]
1010
#![feature(iter_is_partitioned)]
1111
#![feature(rustdoc_internals)]
1212
// tidy-alphabetical-end

compiler/rustc_borrowck/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
33
// tidy-alphabetical-start
44
#![allow(internal_features)]
5+
#![cfg_attr(bootstrap, feature(if_let_guard))]
56
#![doc(rust_logo)]
67
#![feature(assert_matches)]
78
#![feature(box_patterns)]
89
#![feature(file_buffered)]
9-
#![feature(if_let_guard)]
1010
#![feature(negative_impls)]
1111
#![feature(never_type)]
1212
#![feature(rustc_attrs)]

compiler/rustc_builtin_macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
#![allow(internal_features)]
66
#![allow(rustc::diagnostic_outside_of_impl)]
77
#![allow(rustc::untranslatable_diagnostic)]
8+
#![cfg_attr(bootstrap, feature(if_let_guard))]
89
#![cfg_attr(not(bootstrap), feature(autodiff))]
910
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
1011
#![doc(rust_logo)]
1112
#![feature(assert_matches)]
1213
#![feature(box_patterns)]
1314
#![feature(decl_macro)]
14-
#![feature(if_let_guard)]
1515
#![feature(proc_macro_internals)]
1616
#![feature(proc_macro_quote)]
1717
#![feature(rustdoc_internals)]

compiler/rustc_codegen_llvm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
77
// tidy-alphabetical-start
88
#![allow(internal_features)]
9+
#![cfg_attr(bootstrap, feature(if_let_guard))]
910
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
1011
#![doc(rust_logo)]
1112
#![feature(assert_matches)]
1213
#![feature(exact_size_is_empty)]
1314
#![feature(extern_types)]
1415
#![feature(file_buffered)]
15-
#![feature(if_let_guard)]
1616
#![feature(impl_trait_in_assoc_type)]
1717
#![feature(iter_intersperse)]
1818
#![feature(rustdoc_internals)]

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
#![allow(internal_features)]
33
#![allow(rustc::diagnostic_outside_of_impl)]
44
#![allow(rustc::untranslatable_diagnostic)]
5+
#![cfg_attr(bootstrap, feature(if_let_guard))]
56
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
67
#![doc(rust_logo)]
78
#![feature(assert_matches)]
89
#![feature(box_patterns)]
910
#![feature(file_buffered)]
10-
#![feature(if_let_guard)]
1111
#![feature(negative_impls)]
1212
#![feature(rustdoc_internals)]
1313
#![feature(string_from_utf8_lossy_owned)]

compiler/rustc_const_eval/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// tidy-alphabetical-start
22
#![allow(internal_features)]
33
#![allow(rustc::diagnostic_outside_of_impl)]
4+
#![cfg_attr(bootstrap, feature(if_let_guard))]
45
#![doc(rust_logo)]
56
#![feature(assert_matches)]
67
#![feature(box_patterns)]
78
#![feature(decl_macro)]
8-
#![feature(if_let_guard)]
99
#![feature(never_type)]
1010
#![feature(rustdoc_internals)]
1111
#![feature(slice_ptr_get)]

compiler/rustc_errors/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#![allow(internal_features)]
88
#![allow(rustc::diagnostic_outside_of_impl)]
99
#![allow(rustc::untranslatable_diagnostic)]
10+
#![cfg_attr(bootstrap, feature(if_let_guard))]
1011
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
1112
#![doc(rust_logo)]
1213
#![feature(array_windows)]
@@ -15,7 +16,6 @@
1516
#![feature(box_patterns)]
1617
#![feature(default_field_values)]
1718
#![feature(error_reporter)]
18-
#![feature(if_let_guard)]
1919
#![feature(negative_impls)]
2020
#![feature(never_type)]
2121
#![feature(rustc_attrs)]

0 commit comments

Comments
 (0)