RFA-452 · Case file with fixtures · Case 424 of 694 · Compiler evidence
A Rust Pattern Cannot Bind the Same Identifier Twice
Repeated names in patterns do not mean equality. Bind each position with a unique name, then express equality in a guard or after destructuring so the comparison and ownership remain explicit.
- 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
- Identifier patterns create locals rather than logical unification variables, so repeating a name cannot express equality between two independently owned places.
- First discriminating check
- Give each position a unique binding and express equality with a small guard or an ordinary comparison after destructuring.
I tried to express equality with the tuple pattern (value, value). Rust reported E0416 because the identifier value is bound more than once in one pattern.
The failing fixture captures a common expectation from languages where repeating a logical variable constrains two positions. Rust identifier patterns do not work as unification variables. Each bare name introduces a local binding.
A binding names one selected place
The identifier-pattern Reference requires an identifier to be unique within a pattern. If two tuple positions both said value, Rust would need one local to refer to two source places.
Even if their current contents are equal, those places have different identities and may have different ownership consequences. The pattern stage binds structure; it does not infer a comparison between arbitrary bound values.
Use a guard for equality
The repaired fixture binds two names and compares them:
match pair {
(left, right) if left == right => true,
_ => false,
}
The guard makes the semantic operation explicit. First the tuple structure matches and creates left and right. Then PartialEq decides whether their values compare equal.
This separation also gives me a clear place to choose case-insensitive comparison, canonicalisation, tolerances, or another domain rule instead of assuming raw equality.
A guard is not structural exhaustiveness
The Rust Book's match-guard section explains that a guard is an additional condition after a pattern matches.
Because the condition can be false, another arm is needed. (left, right) if left == right does not cover every tuple even though the tuple portion alone would.
I make the unequal case explicit rather than relying on a guard as if it narrowed the type permanently.
Constants can express one known repeated value
If both positions must equal one compile-time constant, I can use a constant pattern in each position:
const ZERO: u8 = 0;
match pair {
(ZERO, ZERO) => "both zero",
_ => "other",
}
This does not bind ZERO; it matches a structural constant. It is different from asking whether two otherwise unknown values equal each other.
Qualified constants are often clearer because they cannot be mistaken for fresh lowercase bindings.
Ownership is clearer with two names
Suppose the tuple contains two String values. Binding both by value moves two separate strings. A single repeated name could not honestly describe which moved object it owns.
With left and right, I can compare borrowed forms or match a reference to the tuple. If the values must remain available, I arrange borrowing before adding the equality guard.
The E0416 rule therefore prevents ambiguous ownership as well as ambiguous naming.
Nested patterns follow the same rule
The duplicate may be far apart inside a struct, enum, or nested tuple. A large generated destructure can bind id in two branches of the same structural pattern accidentally.
I reduce the pattern and search only the binding positions. Field labels before colons are not bindings themselves; the local names on the right are. This distinction also separates E0416 from E0025, where one source struct field is selected twice.
Equality may belong after the match
If the match has no other structural alternatives, I often destructure first and use a normal if:
let (left, right) = pair;
if left == right { /* ... */ }
This avoids using match syntax only as a comparison device. The best repair is the one that keeps the decision easy to read.
My E0416 checklist
I keep guards small and side-effect free when they express relationships between bindings. A guard that calls distant services or mutates state makes pattern selection harder to reason about and test. Equality, ordering, and a short predicate are natural. Larger validation usually belongs in a named function before or inside the arm, with an explicit error path. This keeps the structural part of the match useful as a quick map of the possible data shapes.
- Where is the identifier introduced more than once?
- Did I intend equality rather than two bindings?
- Should equality use a guard or ordinary
if? - Do I need raw
PartialEqor a domain-specific comparison? - Are constants a better structural expression for known values?
- Will binding by value move either component?
- Is the duplicate hidden inside generated or nested syntax?
The core principle is that patterns decompose structure and create names; they do not unify repeated logical variables. I bind each place once, then write the relationship between those values as an explicit operation the type system and the reader can inspect.