Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-068 · Case file with fixtures · Case 40 of 694 · Compiler evidence

E0207: Why a Generic Parameter Used in a Method Is Still Unconstrained on the Impl

An impl-level parameter must identify the implemented self type or trait relationship. If callers choose the type per operation, place it on the method; if values carry it, place it on the self type.

Reviewed
Rust
Rust 1.98.1
Targets
all targets
Profiles
dev, release, test

Direct answer

What this Rust failure means

Why it happens
The implementation block does not identify which instance of the self type it belongs to, so `Marker::method` cannot select one value for T.
First discriminating check
Move `T` onto the method or add it to the self type, then check whether type selection belongs to each call or to each constructed value.

Using a type parameter inside a method body does not necessarily constrain an implementation block:

struct Marker;

impl<T> Marker {
    fn name() -> &'static str {
        std::any::type_name::<T>()
    }
}

Rust 1.98.1 reports E0207 at T: the type parameter is not constrained by the impl trait, self type, or predicates. The failing fixture contains this exact inherent implementation.

T is used, so the message can sound wrong. The question is not whether text in the block mentions T. The question is how Rust selects one implementation when code names Marker::name().

Impl selection has no place to learn T

The self type is always the same zero-sized Marker. There is no implemented trait with T in its arguments. Before entering the method body, the compiler sees an open family of indistinguishable blocks:

impl Marker for T = u8
impl Marker for T = String
impl Marker for T = EveryOtherType

All define the same associated function name on the same self type. A return type of &'static str also does not reveal which T the caller wanted. The implementation parameter is therefore unconstrained at the selection boundary.

The Reference rules for generic implementations require parameters to constrain the implementation through the trait, implementing type, or an associated-type equality in the relevant form.

Put a per-call choice on the method

If each invocation chooses a type, the generic belongs to the function:

impl Marker {
    fn name<T>() -> &'static str {
        std::any::type_name::<T>()
    }
}

let name = Marker::name::<u8>();

The turbofish now applies to a generic method, and its T is selected for this call. The repaired fixture compiles, runs, and verifies the name on Rust 1.98.1.

This is the right model for stateless operations such as decoding a requested type, constructing a value, or querying type metadata.

Put a value-level choice on the self type

If each marker value represents one type, T belongs to the struct:

use std::marker::PhantomData;

struct Marker<T>(PhantomData<fn() -> T>);

impl<T> Marker<T> {
    fn name(&self) -> &'static str {
        std::any::type_name::<T>()
    }
}

Now Marker<u8> and Marker<String> are distinct self types, so implementation selection is unambiguous. PhantomData also communicates the logical relationship to variance, auto traits, and drop checking; its exact form should match how the marker conceptually uses T.

I do not add PhantomData<T> only to silence E0207. It changes the type's semantics. If no value needs to remember T, a generic method is simpler.

Trait arguments can constrain the block

This is also valid:

trait NameOf<T> {
    fn name() -> &'static str;
}

impl<T> NameOf<T> for Marker {
    fn name() -> &'static str {
        std::any::type_name::<T>()
    }
}

T appears in the implemented trait, so <Marker as NameOf<u8>>::name() selects a concrete relationship. This design is useful when downstream code is generic over the relationship itself, but it is more ceremony for a local utility.

An implementation may try to define an associated output based on a parameter not uniquely determined by the input trait and self type. Two different choices could then assign different associated types to the same trait implementation. Rust rejects that ambiguity for the same coherence reason.

I work backward from the lookup expression: given the self type and trait arguments visible there, can the compiler determine every impl-level parameter uniquely? A parameter mentioned only in a method body, where-clause with multiple possible witnesses, or associated output may fail that test.

My decision rule

  • The caller chooses T for each operation: method parameter.
  • Constructed values carry the identity of T: self-type parameter.
  • A trait relationship is indexed by T: trait parameter.
  • The implementation chooses one output for a known input: associated type, when uniquely determined.

The official E0207 explanation shows all these positions. I find it more useful to think about selection than textual use. Rust needs to locate one implementation before running or type-checking its method for a particular call. Put the generic parameter where that lookup can actually see it.