Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-516 · Case file with fixtures · Case 488 of 694 · Compiler evidence

Generic Associated Constants Cannot Be Used Directly as Rust Patterns

Rust checks generic match structure before one implementation selects an associated constant. Bind the candidate and compare in a guard when equality, rather than structural pattern identity, is the requirement.

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
Different implementations may select different constant values after the generic match structure and exhaustiveness must already have been validated.
First discriminating check
Bind the candidate and compare it in a guard or policy predicate, then retain structurally exhaustive fallback arms and verify equality semantics.

I used P::ACTIVE as a match pattern inside a generic function. Rust emitted E0158 because that constant depends on the unknown implementation of generic parameter P.

The failing fixture seems reasonable: every policy chooses one State. The problem is when and how Rust validates patterns and exhaustiveness.

Generic bodies are checked before one P is selected

Rust type-checks the generic function as a valid definition for every permitted P. Different implementations can assign different values to ACTIVE.

A constant pattern participates in structural matching and exhaustiveness analysis. The compiler cannot treat one generically selected value as a fixed variant while proving the match covers the enum.

The official E0158 page recommends a guard for this situation.

The repaired fixture binds then compares

The repaired fixture uses candidate if candidate == P::ACTIVE. The pattern itself is a normal binding, and the guard performs an equality expression after matching reaches that arm.

This requires the value type to implement PartialEq. The fixture derives it and checks both matching and non-matching states.

The repair models a predicate, which is exactly what the generic constant supplies here.

Guards do not contribute to exhaustiveness

Because a guard can evaluate false, Rust does not generally treat it as covering a variant for exhaustiveness purposes. I keep a fallback arm or other structurally exhaustive patterns.

This is important when rewriting E0158: replacing a constant pattern with a guard may require an _ arm even if I know one policy value always matches some case.

The Reference on match guards explains their conditional role.

A concrete constant can remain a pattern

The issue is not that constants are forbidden in patterns. Suitable concrete constants can be used when their value and structural eligibility are known at the pattern definition.

The generic projection delays selection. A static also has different identity and mutation semantics and should be compared through a guard rather than treated like an inline structural constant.

I distinguish fixed compile-time identity from a value selected through abstraction.

Matching variants may express the domain better

If policies select only among enum variants, another design can pattern-match variants directly and place policy in a method such as P::is_active(state). This lets each implementation express more complex rules than one constant.

Alternatively, the caller can pass the active value and the function perform equality without a match. I do not keep match syntax when the operation is simply boolean comparison.

The clearest control flow depends on whether other variant-specific arms exist.

Generic const parameters face the same timing issue

A const generic value may also be unknown while the generic body is checked. Treating it as a pattern can run into the same restriction.

I bind and compare, or redesign the operation around ordinary const expressions where generic parameters are supported. Monomorphisation later does not erase requirements imposed during generic definition checking.

This timing model explains many “but the value is known eventually” questions.

Equality semantics must be intentional

Moving from a constant pattern to == relies on the type's PartialEq implementation. For a simple fieldless enum this is straightforward. For floating-point data, custom domain types, or values with ignored metadata, equality may not express the original intention.

I inspect or define comparison semantics before adopting the guard. A policy could instead expose fn matches(state: &State) -> bool, keeping domain-specific equivalence with the policy owner. This also permits ranges or sets rather than one selected constant. The guard then reads candidate if P::matches(&candidate), while an unguarded fallback preserves exhaustiveness.

Order guarded arms carefully

An earlier broad pattern can make the guarded arm unreachable, and several guards can overlap. I test boundary values and review arm order as control flow, not only as syntax. The compiler's structural exhaustiveness analysis cannot prove every semantic relation among guard predicates.

My E0158 checklist

  • Does the pattern depend on an associated const, const generic, or static?
  • Is the surrounding function checked generically?
  • Do I need structural pattern identity or only equality?
  • Can a binding plus guard express the predicate?
  • Does the value type implement the needed comparison trait?
  • Is the match still exhaustive when guards can fail?
  • Would a policy method describe richer behaviour?
  • Could a plain boolean expression replace the match entirely?

The core principle is that patterns must be validated while generic code is defined. A generically selected value is better treated as data in a guard than as fixed structure in the pattern matrix.