RFA-447 · Case file with fixtures · Case 419 of 694 · Compiler evidence
A Rust Function Call Cannot Be Used as a match Pattern
Patterns inspect supported value structure without executing arbitrary constructors. Match an enum variant or constant, use a guard for computed equality, or call the function before the match when it should produce the scrutinee.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- Patterns inspect supported structural forms without running arbitrary code, whereas an associated function computes and returns a value.
- First discriminating check
- Determine whether the name is a real variant or tuple struct; otherwise match public structure, use a constant, or put computation in a guard.
I saw Event::new() produce Event::Ready and tried to put that call on the left side of a match arm. Rust reported E0164 because function calls are not patterns.
The failing fixture makes the visual ambiguity clear. Parentheses after a tuple variant belong in pattern syntax, but parentheses after an associated function name remain a call expression.
Patterns do not execute arbitrary code
A pattern can inspect literals, variants, fields, tuples, slices, ranges, references, constants, and bindings according to Rust's pattern grammar. It does not call a function to produce a comparison value.
The E0164 explanation says only tuple structs and tuple variants can be used with the shown parenthesised pattern form.
This keeps matching structural and allows the compiler to analyse exhaustiveness, moves, and bindings without evaluating arbitrary user code for each arm.
Match the variant that describes structure
The repaired fixture calls Event::new() to create the scrutinee and matches Event::Ready as a unit variant.
For a tuple variant such as Event::Data(bytes), the pattern can bind bytes. For a struct variant, it names fields. The enum declaration defines which syntactic shape is valid.
An associated constructor may return any variant depending on arguments or state; its name does not become a new pattern synonym.
Use a const for structural value matching
Constants of suitable structural types can appear as path patterns. If the wanted value is fixed at compile time and permitted in patterns, a named const can communicate it.
A static is not interchangeable with a const in pattern resolution, and custom equality rules can restrict which values are structural. I compile the exact type rather than assuming every PartialEq value can become a const pattern.
The RFA-449 case covers the static-versus-binding ambiguity directly.
Use a guard for computed conditions
When the expected value requires a function call, a guard can compare after binding:
match event {
candidate if candidate == Event::new() => { /* ... */ }
_ => {}
}
The guard is runtime code and may call functions. It does not contribute guaranteed structural coverage, so a fallback arm remains necessary.
I avoid expensive or side-effecting guards because they can be evaluated as arms are considered. Precomputing the expected value once is clearer when construction has cost.
Call first when construction defines the input
Sometimes the call belongs on the scrutinee side:
match Event::new() {
Event::Ready => {}
}
This answers “what did the constructor return?” The failing form looked like it answered “does this value equal whatever the constructor returns?” Those are different control-flow questions.
I position computations before or after matching according to which value is being classified.
Constructors are not necessarily injective
Two different constructor calls can produce the same variant, and one constructor can produce several variants. Treating function names as patterns would not reliably expose value structure or bindings.
If domain meaning needs named classifications beyond raw variants, methods such as is_ready, predicate guards, or a second classification enum can make the rule explicit.
Macros can generate patterns, functions cannot
A macro invoked in pattern position may expand to valid pattern syntax. This happens during compilation before pattern analysis. It is not comparable to calling a runtime function.
Pattern macros should expand only to accepted pattern grammar and preserve useful spans. I test their expansion across all supported forms because a diagnostic can otherwise point only at the invocation.
The patterns Reference lists the accepted categories, while the call-expression Reference describes function calls as expressions.
My E0164 checklist
When I see constructor-looking syntax in a pattern, I check whether the name is a real tuple struct or enum variant, or only a function returning one. Those two interfaces can look identical at call sites but expose different information to matching. A public smart constructor may intentionally hide representation and validate inputs. In that case I should not work around the abstraction merely to obtain a pattern. I use public accessors, a predicate in a guard, or a method returning a meaningful enum. This keeps the match aligned with the contract the type author chose to expose.
- Does the path name a tuple variant, tuple struct, or ordinary function?
- Should the call produce the scrutinee instead?
- Is there a real enum variant to match structurally?
- Could a suitable const express a fixed value?
- Should a guard perform computed equality?
- Is the guard expensive or side-effecting?
- Did a macro expand to the wrong syntactic category?
The core principle is that patterns describe value shape; function calls compute values. Rust keeps those operations separate so matching remains analysable. I match declared structure, and I move computation to the scrutinee or a deliberate guard.