Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-437 · Case file with fixtures · Case 409 of 694 · Compiler evidence

An Associated Item Generic Cannot Shadow Its Trait's Parameter

Every generic name in an item's visible scope must identify one parameter. Rename an independent method type, or remove it and reuse the trait's T when sameness was intended; the choice changes the API contract.

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 outer T already names the implementation-level choice, while redeclaration would hide whether the method reuses that type or introduces an independent call-level type.
First discriminating check
Remove the inner parameter when sameness is intended, or rename it and document who chooses each type when it is independent.

I used T for a trait parameter and reused T inside one method's generic list. Rust rejected the method with E0403 because the second declaration shadows a generic parameter already visible from the trait.

The failing fixture reads trait Convert<T> { fn convert<T>(...) }. Humans can guess that the names may mean different things, but a signature must say which relationship is intended.

One name must have one meaning in scope

The E0403 explanation covers duplicate generic names in one parameter list and associated items shadowing containing parameters.

Within Convert<T>, the name T already refers to the type chosen for that trait implementation. Redeclaring it on convert would hide that meaning exactly where readers need to distinguish trait-level and call-level choices.

Rust rejects the ambiguity instead of applying ordinary block-variable shadowing rules.

Rename when the method chooses another type

The repaired fixture uses U for the method parameter:

trait Convert<T> {
    fn convert<U>(&self, value: U);
}

Now an implementation chooses trait-level T once, while every method call may choose its own U. The fixture implements Convert<u8> and calls the method with &str, proving the two roles differ.

Names such as Input, Output, Source, and Target are often clearer than letters in a public API.

Remove the inner generic when types should match

Perhaps the original intent was for convert to consume the trait's T:

trait Convert<T> {
    fn convert(&self, value: T);
}

This is a different contract. Each implementation handles the one T named by its trait path. The caller cannot choose a new input type per invocation.

I decide between these repairs from the domain, not from whichever edit is shortest.

Trait parameters and method parameters vary at different times

For impl Convert<u8> for Decoder, the trait parameter is fixed for that implementation. A generic method remains universally quantified for calls on the decoder.

This distinction affects object compatibility, inference, implementation overlap, and documentation. Generic methods are not dispatchable through ordinary trait objects unless excluded with an appropriate Self: Sized boundary.

Moving a parameter from method to trait can therefore enable or change dynamic dispatch, but it can also require separate implementations for every input type.

Associated types offer another cardinality

If each implementor has exactly one output type, an associated type may express that more directly:

trait Convert<Input> {
    type Output;
    fn convert(&self, value: Input) -> Self::Output;
}

The relationship says one output per Self and Input implementation. A generic method can instead return different types only through additional abstractions.

Parameter placement is a model of who chooses and how often, not only syntax.

Lifetimes and const parameters need distinct names too

The same broad clarity applies to lifetime and const generic names in nested declarations. I use 'input and 'output when their relationship matters, and descriptive const names such as WIDTH or LANES.

Short names work in small local functions, but shadowing a containing declaration would still erase meaning. The generics Reference defines the parameter namespaces and scopes.

Macro generation can accidentally repeat names

Procedural and declarative macros often combine user generics with generated method generics. A hard-coded T can collide with a user parameter.

I generate deliberately uncommon internal identifiers with suitable hygiene and test inputs already using common names such as T, U, and N. Better still, I reuse parsed generic declarations according to the semantic role rather than manufacturing duplicates.

Documentation should state the quantifier in words

For public traits I write whether an implementation handles one chosen input type or every input type satisfying a bound. This small sentence exposes parameter placement mistakes earlier than compiler errors. “For any U” describes a method generic; “for the T implemented by this converter” describes a trait generic. The source syntax and the prose should agree.

My E0403 checklist

  • Which outer item already declares the name?
  • Is the inner parameter meant to be identical or independent?
  • Who chooses each type: implementation or method caller?
  • Would an associated type express one fixed result better?
  • Does moving the parameter affect dyn compatibility?
  • Did macro-generated code collide with user generics?
  • Can role-based names make the API readable?

The core principle is that parameter scope carries type relationships. Rust refuses generic shadowing because replacing one meaning with another inside a signature hides those relationships. I rename independent parameters and remove duplicate declarations when sameness is intended.