RFA-455 · Case file with fixtures · Case 427 of 694 · Compiler evidence
An Associated Function Item Is Not a Rust Value Pattern
A function item is callable behaviour, not the value it may return. Use a constant for structural matching, or bind the scrutinee and compare it with a function result in a guard when computation is required.
- 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
- The path identifies callable code rather than the u8 produced by executing that code, and patterns do not implicitly perform calls.
- First discriminating check
- Use an associated const when fixed data is the real contract, or evaluate once and compare in a guard when runtime computation is intentional.
I wrote Parser::default_code as a match pattern, without parentheses, expecting Rust to compare against the value returned by that function. Rust emitted E0533 because the path names an associated function item, not a unit value, variant, or constant.
The failing fixture complements the function-call pattern failure: adding parentheses would be an expression and also invalid in a pattern, while removing them names the function itself rather than its result.
A function item is a callable value
Parser::default_code identifies code that can be called. It has a function-item type and can coerce to a function pointer in suitable expression contexts. It is not the u8 produced by invoking it.
The function item Reference distinguishes the declared function from evaluation of a call. A match over u8 therefore cannot treat the function path as the returned zero.
This distinction remains even when the function is pure, takes no arguments, and always returns the same value.
Use a guard when comparison requires execution
The repaired fixture binds the input and performs the call in a guard:
match code {
value if value == Parser::default_code() => "default",
_ => "other",
}
The pattern stage binds value; the guard is normal runtime evaluation. This makes timing and fallibility visible.
Because a guard may be false, I retain a fallback arm. Guards do not contribute unconditional exhaustiveness.
Prefer a constant for a structural fixed value
If the value is truly fixed and requires no computation, an associated constant expresses the intended contract better:
impl Parser {
const DEFAULT_CODE: u8 = 0;
}
match code {
Parser::DEFAULT_CODE => "default",
_ => "other",
}
The path-pattern rules permit suitable constants because the compiler can treat them as structural values. The uppercase name also signals comparison rather than a fresh binding.
I do not change a function into a constant if it genuinely depends on configuration, target, or runtime state.
Method calls need a receiver too
An instance method path such as Parser::code is still a function item. Calling it may need a receiver and may observe state. It cannot become a pattern simply because the method eventually returns a comparable value.
I calculate or retrieve the comparison value before the match when doing so once is clearer:
let expected = parser.code();
match code {
value if value == expected => { /* ... */ }
_ => { /* ... */ }
}
This also prevents repeated work across several guards.
Function pointer matching is not function-result matching
If the scrutinee itself is a function pointer, ordinary equality is limited and usually not a sound basis for program identity across code generation units. The important point is that matching the pointer would still be different from matching what calling it returns.
I model commands or handlers with an enum or explicit identifier when stable identity matters. Behavioural values are not a substitute for a domain tag.
API shape communicates stability
A constant says callers may rely on a fixed value. A function leaves room for calculation or future policy changes. Converting between them only to satisfy pattern syntax can accidentally widen a compatibility promise.
I choose the public API first, then use a guard, precomputation, or enum representation that respects it.
My E0533 checklist
I also consider error handling in the computation. A function returning Result<u8, Error> cannot be hidden inside an equality pattern even conceptually. I evaluate it before the match, propagate or classify the error, and then match the successful value. This ordering exposes work that can fail and avoids repeating it in multiple guards. It also keeps the match focused on a value that is already available, which is the mental model Rust patterns are designed around.
- Does the path name a function, method, constant, or unit value?
- Am I trying to match the callable or its returned value?
- Is the returned value truly constant by contract?
- Should a guard perform runtime comparison?
- Would precomputing avoid repeated calls or side effects?
- Does the function depend on configuration or mutable state?
- Would an enum represent stable identity better?
The core principle is that code and data remain different even when code always returns the same data today. A pattern describes supported value structure. When obtaining the comparison value requires execution, I make that execution explicit outside the pattern.