Skip to content

Tracking Issue for assert_matches #82775

Closed

Description

@m-ou-se

View all comments

Feature gate: #![feature(assert_matches)]

This is a tracking issue for the assert_matches!() and debug_assert_matches!() macros.

Public API

// core

macro_rules! assert_matches { .. }
macro_rules! debug_assert_matches { .. }

Steps / History

Unresolved Questions

  • Add => expr syntax?
    • Nope, too confusing. (See discussion below.)

Activity

  1. added
    T-libs-api[DEPRECATED; DO NOT USE]
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on Mar 4, 2021
  2. m-ou-se commented on Mar 9, 2021

    @m-ou-se
    MemberAuthor

    Several implementations of assert_matches!() in the ecosystem also accept an => expr syntax, to do something with the matched values or even return something from the macro.

    This is currently not supported by std::assert_matches!() as implemented in #82770.

    Adding it would allow things like:

    let thing = assert_matches!(thing, Some(x) => x);

    and

    assert_matches!(thing, Some((x, y)) => {
        assert_eq!(x.a, 10);
        assert_eq!(y.b(), 20);
    });
  3. drahnr commented on Mar 11, 2021

    @drahnr
    Contributor

    As a (almost) daily user of the assert_matches crate, I have to admit that without the additional $pat => $expr syntax, for most me it would be still impractical to use.

    I.e. asserting a certain sequence of messages with a mocked context and assuring the messages have expected content (which by design may not impl {Partial,}Eq) I would usually use (super simplified):

    type A = u8;
    struct B(u8);
    struct C(u8);
    struct D(u8);
    
    enum Msgs {
    Specific { a: A, b: B, c: C, d: D},
    // snip
    }
    assert_matches!(rx.next().unwrap(), Msgs::Specific { a, b: B(b), .. } => { assert_eq!(a, b); })
    assert_matches!(rx.next().unwrap(), Msgs::Specific { b, c: C(c), .. } => { assert_eq!(b, c); })
    ..

    so for this the extension with => is required to replace the crate assert_matches in practice from my experience.

  4. m-ou-se commented on Mar 11, 2021

    @m-ou-se
    MemberAuthor

    In cases like that, the if guard on the pattern also works, right?

    assert_matches!(rx.next(), Some(Msgs::Specific { a, b: B(b), .. }) if a == b);
  5. drahnr commented on Mar 23, 2021

    @drahnr
    Contributor

    While your approach is valid for simple cases, I disagree for the general use case. This is not very ergonomic as soon as there are various sub patterns, that have to be asserted against transformed struct member values or larger enums with multiple elements common for messaging enums.

    assert_matches!(rx.next(), Some(Msgs::Specific { a, b: B(b), .. }) => {
      assert_eq!(a, b);
      let container = transform($container);
      if container.is_empty() {
           assert!(..);
      } else {
          assert!(..);
      }
    });
    ..

    It could still be expressed with boolean expression combinators using the current impl, yet that adds a lot visual complexity that imho should not exist in a test case.

    Note that I am not saying the impl is bad or anything :) - I would very much like to see assert_matches! in std! This effort is much appreciated!

  6. m-ou-se commented on Mar 23, 2021

    @m-ou-se
    MemberAuthor

    @rust-lang/libs Ping for opinions.

    Should assert_matches accept => { .. } blocks to allow for nested asserts (and other code)? The existing assert_matches macros in the ecosystem accept that. But I personally feel it makes things a bit too complicated and unclear.

  7. BurntSushi commented on Mar 23, 2021

    @BurntSushi
    Member

    It's an interesting extension. At first it looked a bit odd to me, but after ruminating on it a bit, it does seem fairly natural to me.

    In terms of moving forward:

    1. Would adding this later be a backwards compatible addition?
    2. How often is this extension used in practice? In theory, we could search the code on crates.io and count the ratio between uses of assert_matches! with and without this extension. If the ratio is very small, then maybe we land it without the extension if the answer to (1) is "yes."
  8. joshtriplett commented on Mar 23, 2021

    @joshtriplett
    Member

    This makes me think of the proposal to add => syntax to matches!, which the libs team declined.

    This seems very similar, and I feel like the arguments are the same. If we have assert_matches!(x, Some(Variant(a, b)) => a == b), similar use cases would motivate if matches!(x, Some(Variant(a, b)) => a == b) { ... }.

    I personally feel that in both cases the => syntax adds enough complexity to make it worth spelling out a match or if let, instead. I don't really want to see an entire block of arbitrary code embedded inside an assert_matches!. I also think this will become easier to write when we finish if let chaining.

  9. m-ou-se commented on Mar 24, 2021

    @m-ou-se
    MemberAuthor

    assert_matches!(x, Some((a, b)) if a == b) would assert that x is a Some and a and b are equal, but
    assert_matches!(x, Some((a, b)) => a == b) would only assert that x is a Some, and return the value of a == b without asserting anything about it. The difference there is far too subtle for me.

    We could require the => expr to be (), but I don't think that solves the problem of unclear semantics entirely.

  10. joshtriplett commented on Mar 24, 2021

    @joshtriplett
    Member

    assert_matches!(x, Some((a, b)) if a == b) would assert that x is a Some and a and b are equal, but assert_matches!(x, Some((a, b)) => a == b) would only assert that x is a Some, and return the value of a == b without asserting anything about it. The difference there is far too subtle for me.

    For me as well; that was not clear to me at all.

  11. BoxyUwU commented on Jun 4, 2021

    @BoxyUwU
    Member

    A separate macro that always requires a => expr arm i.e. unwrap_matches extract_matches might make it less ambiguous/more clear

  12. gilescope commented on Jul 5, 2021

    @gilescope
    Contributor

    Well if we're agreed on leaving it as is then we can head towards stabilising it...

  13. gThorondorsen commented on Dec 1, 2021

    @gThorondorsen
    @drahnr in #82775 (comment)

    While your approach is valid for simple cases, I disagree for the general use case. This is not very ergonomic as soon as there are various sub patterns, that have to be asserted against transformed struct member values or larger enums with multiple elements common for messaging enums.

    assert_matches!(rx.next(), Some(Msgs::Specific { a, b: B(b), .. }) => {
      assert_eq!(a, b);
      let container = transform($container);
      if container.is_empty() {
           assert!(..);
      } else {
          assert!(..);
      }
    });
    ..

    It could still be expressed with boolean expression combinators using the current impl, yet that adds a lot visual complexity that imho should not exist in a test case.

    Note that I am not saying the impl is bad or anything :) - I would very much like to see assert_matches! in std! This effort is much appreciated!

    Your example can be rewritten to a match guard that always returns true except when it panics, like this.

    -assert_matches!(rx.next(), Some(Msgs::Specific { a, b: B(b), .. }) => {
    +assert_matches!(rx.next(), Some(Msgs::Specific { a, b: B(b), .. }) if ({
         assert_eq!(a, b);
         let container = transform($container);
         if container.is_empty() {
             assert!(/* … */);
         } else {
             assert!(/* … */);
         }
    -});
    +    true
    +}));

    Definitely non-obvious when you don't know about it, but hardly worse to write if you are adding to an existing test suite with the pattern everywhere.

  14. drahnr commented on Dec 1, 2021

    @drahnr
    Contributor

    Re-reading the comments, I fully support the current impl and withdraw my previous concerns.

  15. 109 remaining items

  16. rust-rfcbot commented on Jan 26, 2026

    @rust-rfcbot
    Collaborator

    Error encounted:
    Provided team `` is invalid

  17. Amanieu commented on Jan 26, 2026

    @Amanieu
    Member

    @rfcbot merge libs-api

  18. removed
    disposition-mergeThis issue / PR is in PFCP or FCP with a disposition to merge it.
    on Jan 26, 2026
  19. Amanieu commented on Jan 26, 2026

    @Amanieu
    Member

    @rfcbot merge libs-api

  20. Amanieu commented on Jan 26, 2026

    @Amanieu
    Member

    Started the FCP in #137487.

  21. added a commit that references this issue on Feb 11, 2026
  22. added a commit that references this issue on Feb 12, 2026
  23. jhpratt commented on Apr 17, 2026

    @jhpratt
    Member

    This was destabilized in #154999. Reopening. The reason for destabilization seems to be scoping, which is tracked in #154406.

  24. jhpratt commented on May 27, 2026

    @jhpratt
    Member

    Looking at release notes, it appears as though the scoping was backported to beta and will land on stable tomorrow (in 1.96).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libs-api[DEPRECATED; DO NOT USE]

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions