Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-485 · Case file with fixtures · Case 457 of 694 · Compiler evidence

A Self Associated Type Must Be Declared by the Rust Trait

Self can project only associated items promised by the current trait and its known bounds. Declare Error when it belongs to every source, use the correct existing item, or name another trait that owns it.

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
Self exposes only associated items proven by the current trait, its supertraits, and explicit bounds; naming convention alone proves no Error projection.
First discriminating check
Add a meaningful trait-level Error choice, correct a stale item name, or qualify the other trait whose contract actually supplies the type.

I declared type Item in a Source trait but wrote a method returning &Self::Error. Rust produced E0220 because no associated type named Error is known for Self under that trait contract.

The failing fixture puts the invalid projection inside the trait itself. No implementation can repair the omission because the trait signature fails before implementations are considered.

Self includes only declared capabilities

Inside a trait, Self represents the eventual implementing type. Rust knows it implements the current trait and its supertraits, plus any explicit bounds in context.

The E0220 explanation asks me to declare the associated type or use the correct existing name. Self::Error is not inferred from common naming conventions or future impl bodies.

The projection must be justified by a visible trait requirement.

Declare Error when it is part of every source

The repaired fixture adds:

type Error;
fn error(&self) -> &Self::Error;

Now each implementation chooses an error type and the method signature can refer to it. The associated-type Reference defines this relationship.

I also add useful bounds such as std::error::Error only when generic consumers genuinely require them.

Use the existing item when this is a naming mistake

The compiler may suggest Self::Item because that is the only declared type. I accept that suggestion only if the method truly returns an item rather than an error.

Near-miss names often happen after a trait refactor. Searching all implementations and call sites helps decide whether the declaration or the use was renamed incompletely.

A suggestion proves availability, not semantic intent.

Project through the trait that owns the type

If Error belongs to a supertrait or another bound, I may write <Self as Fallible>::Error to make ownership explicit. The current trait must require Self: Fallible for that projection to be valid.

This is useful when several traits define an Error associated type. Shorthand can become ambiguous, while qualified syntax records the exact contract.

I avoid duplicating the associated type across traits unless equality relationships are carefully defined.

A generic error parameter changes who chooses

trait Source<E> lets the same concrete type implement Source for several error types in principle, while trait Source { type Error; } normally chooses one error type per implementation relationship.

That difference affects inference, coherence, and API use. I choose an associated type when the implementor determines one natural error and a generic parameter when callers or multiple impls need selection.

E0220 does not decide between these designs automatically.

Infallible sources still need a representation

If the trait requires Error but one implementation cannot fail, core::convert::Infallible can represent an uninhabited error type. Whether the trait should instead offer a separate infallible capability depends on how callers handle results.

I do not use () as an error merely to fill the slot if that loses meaningful exhaustiveness.

The type choice becomes visible in generic error conversion.

Public trait evolution is costly

Adding an associated type to a published trait requires existing implementors to define it. This can be a breaking change.

Before making the repair in a library, I consider a new trait, method with a concrete error, extension trait, or versioned API. Internal code has more freedom but still benefits from a migration plan.

My E0220 checklist

I verify implementations with at least two different associated choices. A trait designed only against one concrete type can accidentally assume methods available on that one Error or Item. Generic default methods must use only bounds declared by the trait. Adding a second small implementation is a strong test of whether the abstraction is real and whether the associated type needs another capability bound. This is more informative than a complex mock mirroring the first implementation exactly.

  • Which associated types does the current trait actually declare?
  • Is the name a typo for an existing item?
  • Does every implementation need this concept?
  • Does a supertrait or another bound own it?
  • Should the implementor or caller choose the type?
  • What represents an infallible implementation?
  • Would adding the item break downstream implementors?
  • Are equality bounds needed between same-named projections?

The core principle is that Self::Name is a projection from proven trait capability, not an open namespace on an unknown type. I declare and qualify that capability where it semantically belongs before using the projection in signatures.