Skip to content

Support if-let-guard in matches! and assert_matches!#152832

Open
SpriteOvO wants to merge 1 commit intorust-lang:mainfrom
SpriteOvO:if-let-guard-macros
Open

Support if-let-guard in matches! and assert_matches!#152832
SpriteOvO wants to merge 1 commit intorust-lang:mainfrom
SpriteOvO:if-let-guard-macros

Conversation

@SpriteOvO
Copy link
Member

@SpriteOvO SpriteOvO commented Feb 19, 2026

Closes #152313, and if-let-guard is stabilized in #141295 just now.

Motivations

Simplify code where match and let-else are used for assertions. For examples, see files:

-    const {
-        match Type::of::<(i8, u8)>().kind {
-            TypeKind::Tuple(tup) => {
-                let [a, b] = tup.fields else { unreachable!() };
-
-                assert!(a.offset == 0);
-                assert!(b.offset == 1);
-
-                match (a.ty.info().kind, b.ty.info().kind) {
-                    (TypeKind::Int(a), TypeKind::Int(b)) => {
-                        assert!(a.bits == 8 && a.signed);
-                        assert!(b.bits == 8 && !b.signed);
-                    }
-                    _ => unreachable!(),
-                }
-            }
-            _ => unreachable!(),
-        }
-    }
+    assert_matches!(
+        const { Type::of::<(i8, u8)>().kind }, TypeKind::Tuple(tup)
+            if let [a, b] = tup.fields
+            && a.offset == 0
+            && b.offset == 1
+            && let (TypeKind::Int(a), TypeKind::Int(b)) = (a.ty.info().kind, b.ty.info().kind)
+            && a.bits == 8 && a.signed
+            && b.bits == 8 && !b.signed
+    );
     struct TestStruct {
         first: u8,
         second: u16,
         reference: &'static u16,
     }
-    const {
-        let Type { kind: Struct(ty), size, .. } = Type::of::<TestStruct>() else { panic!() };
-        assert!(size == Some(size_of::<TestStruct>()));
-        assert!(!ty.non_exhaustive);
-        assert!(ty.fields.len() == 3);
-        assert!(ty.fields[0].name == "first");
-        assert!(ty.fields[0].ty == TypeId::of::<u8>());
-        assert!(ty.fields[0].offset == offset_of!(TestStruct, first));
-        assert!(ty.fields[1].name == "second");
-        assert!(ty.fields[1].ty == TypeId::of::<u16>());
-        assert!(ty.fields[1].offset == offset_of!(TestStruct, second));
-        assert!(ty.fields[2].name == "reference");
-        assert!(ty.fields[2].ty == TypeId::of::<&'static u16>());
-        assert!(ty.fields[2].offset == offset_of!(TestStruct, reference));
-    }
+    assert_matches!(
+        const {Type::of::<TestStruct>() }, Type { kind: Struct(ty), size, .. }
+           if size == Some(size_of::<TestStruct>())
+           && !ty.non_exhaustive
+           && ty.fields.len() == 3
+           && ty.fields[0].name == "first"
+           && ty.fields[0].ty == TypeId::of::<u8>()
+           && ty.fields[0].offset == offset_of!(TestStruct, first)
+           && ty.fields[1].name == "second"
+           && ty.fields[1].ty == TypeId::of::<u16>()
+           && ty.fields[1].offset == offset_of!(TestStruct, second)
+           && ty.fields[2].name == "reference"
+           && ty.fields[2].ty == TypeId::of::<&'static u16>()
+           && ty.fields[2].offset == offset_of!(TestStruct, reference)
+    );

Notes

At the moment, it seems we cannot fully port const-eval only operations (e.g. file https://github.com/rust-lang/rust/blob/e0cb264b814526acb82def4b5810e394a2ed294f/library/coretests/tests/mem/type_info.rs) to use assert_matches! with if-let-guards, because of:

  • assert_matches!(
        const { Type::of::<(i8, u8)>().kind }, TypeKind::Tuple(tup)
            if let [a, b] = tup.fields
            && let (TypeKind::Int(a), TypeKind::Int(b)) = (a.ty.info().kind, b.ty.info().kind)
    );

    triggers a runtime panic

    pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type {
    panic!("`TypeId::info` can only be called at compile-time")
    }

  • const {
        assert_matches!(
            Type::of::<(i8, u8)>().kind, TypeKind::Tuple(tup)
                if let [a, b] = tup.fields
                && let (TypeKind::Int(a), TypeKind::Int(b)) = (a.ty.info().kind, b.ty.info().kind)
        );
    }

    triggers error[E0015]: cannot call non-const function 'core::panicking::assert_matches_failed::<_>' in constants. (blocked by const functions can assert! but cannot assert_eq! #119826)

Todo

  • Documentation updates.

r? theemathas
cc @rustbot ping libs-api

@SpriteOvO SpriteOvO added A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. A-patterns Relating to patterns and pattern matching F-if_let_guard `#![feature(if_let_guard)]` labels Feb 19, 2026
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 19, 2026
@rustbot

This comment was marked as off-topic.

@SpriteOvO
Copy link
Member Author

cc @rust-lang/libs-api

@theemathas theemathas added the needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. label Feb 19, 2026
@fmease
Copy link
Member

fmease commented Feb 19, 2026

r? libs

@rustbot rustbot added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Feb 19, 2026
assert!(
matches!(Some(Some(2_i32)), Some(inner) if let Some(value) = inner && value.pow(2) == 4)
);
assert_matches!(Some(Some(2_i32)), Some(inner) if let Some(value) = inner && value.pow(2) == 4);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs another test for assert_matches!() with a panic message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so looks like we can't simply use $(tt)+ to catch if-let guards because it also catches the comma and panic message.

Does this mean we have to start implementing it as a compiler built-in macro? Especially since we already have 4 arms here.

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

Labels

A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-patterns Relating to patterns and pattern matching F-if_let_guard `#![feature(if_let_guard)]` needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support if let guards in matches!() and assert_matches!()

5 participants

Comments