Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-431 · Case file with fixtures · Case 403 of 694 · Compiler evidence

A Trait impl Must Match the Trait's impl Trait Parameter Form

Argument-position impl Trait creates an anonymous parameter whose declaration form is part of trait signature matching. Use the same form on both sides, or change the trait and every implementation to one explicit named generic design.

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
Argument-position impl Trait creates an anonymous parameter whose declaration form participates in exact trait item signature matching.
First discriminating check
Choose named generics or impl Trait at the trait boundary and repeat the same generic parameter structure in every implementation.

I knew that argument-position impl Trait is closely related to a generic parameter, so I mixed the two forms across a trait and its implementation. Rust reported E0643: expected impl Trait, found a generic parameter.

The failing fixture declares the trait method with &impl Iterator<Item = u8>. Its implementation introduces I: Iterator<Item = u8> and accepts &I. The apparent capability is the same, but the signatures do not match in the required form.

Anonymous does not mean interchangeable everywhere

The impl Trait Reference explains that argument-position impl Trait introduces an anonymous type parameter.

For a standalone function, I can often choose either syntax according to whether the type needs a name. For an implementation of an existing trait, I am not declaring an independent function. I am defining the item promised by the trait, and its generic parameter structure must match.

The E0643 explanation calls out this exact mismatch.

Pick one form at the trait boundary

The repaired fixture changes both trait and impl to a named generic:

trait Inspect {
    fn inspect<I: Iterator<Item = u8>>(&self, values: &I);
}

The implementation repeats that shape. The name I itself does not need to match between declarations; its position, bounds, and role do.

An alternative repair is to keep impl Iterator in both trait and impl. I prefer a named generic when documentation, additional where bounds, or relationships with other parameters benefit from the name.

Same capability is not enough for signature matching

Trait dispatch needs an exact declaration-level contract. If implementations could independently reshape generic parameters, lifetime binding and type relationships could differ in ways that are not visible to callers.

This is related to E0053 for ordinary parameter mismatches and E0276 for stronger impl bounds. E0643 focuses on the generic-versus-anonymous impl Trait form.

I read these diagnostics together as one rule: an impl body can vary, but the call boundary belongs to the trait.

Use a name when positions must relate

Suppose an inspector accepts two iterators of the same concrete type. Two separate impl Iterator positions can introduce separate anonymous types. A named parameter makes sameness explicit:

fn compare<I: Iterator<Item = u8>>(&self, left: &I, right: &I);

If different iterator types are permitted, I use two names or two anonymous positions intentionally.

Return types, associated types, and where clauses often make these relationships important. Compact syntax is useful only while it keeps the contract obvious.

This is still static dispatch

Neither impl Iterator nor I: Iterator here creates a trait object. The caller supplies a concrete iterator type, and ordinary generic monomorphisation applies.

Dynamic dispatch would use something like &mut dyn Iterator<Item = u8>. That changes sizing, object lifetime, dispatch, and which methods are callable. I do not use dyn as a spelling repair for E0643.

Public API evolution needs coordination

Changing a public trait from anonymous to named generic syntax can affect source compatibility and downstream implementations even when most callers see similar behaviour. I update the trait, every in-tree impl, examples, mocks, feature-gated backends, and downstream migration notes together.

Default methods and blanket implementations deserve attention because they may carry their own bounds. I compile the full feature matrix rather than trusting one implementation.

Macros can generate the mismatch

A macro may emit impl Trait in the trait and named parameters in implementations. The diagnostic points at generated code, but the fix belongs in the common template.

I keep one signature representation in macro input and generate both sides from it. Duplicating token fragments invites drift, especially when associated types or lifetimes are added later.

My E0643 checklist

  • Does the trait use impl Trait or named generics?
  • Does the impl repeat the same parameter structure?
  • Are bounds and associated-type bindings equivalent and positioned alike?
  • Must multiple arguments share one concrete type?
  • Is dynamic dispatch actually intended?
  • Did a macro or feature-gated implementation drift?
  • Does an API migration need downstream coordination?

The core principle is that anonymous generics are still real parts of a signature. A trait chooses the callable structure once. I use the same structure in every implementation, and I introduce names at the trait level when the relationship between types needs to be expressed.