Mehdi Akiki
Rust Failure Atlas / Language and diagnostics

RFA-490 · Case file with fixtures · Case 462 of 694 · Compiler evidence

A Rust Trait Impl Must Provide Every Required Item

A trait implementation is complete only when every required method, associated type, and associated constant has a value. Defaults make an item optional to override; declarations without defaults do not.

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
Naming a trait in an impl creates a full contract obligation, and every non-default method, associated type, constant, and function slot must be supplied.
First discriminating check
Read the exact resolved trait definition and the compiler's missing-item list, then implement real behaviour or reconsider whether this type should promise the trait.

I wrote impl Reporter for HealthCheck {} while Reporter required a report method. Rust emitted E0046 because naming a trait in an impl does not automatically create the behaviour promised by that trait.

The failing fixture is small, but I meet this failure often after a trait grows or when an implementation is first scaffolded. The important words in the diagnostic are the list after missing:. They are the unfinished part of the contract.

A trait impl is a completed contract

The Rust Reference on trait implementations says an impl must define all non-default associated items, may replace default ones, and cannot add unrelated items. This gives me a precise model: the trait declares slots and the impl fills the required slots for one type.

Those slots are not only methods. A trait can require an associated type, an associated constant, and functions without a self receiver. E0046 may list several missing names when more than one is absent.

I therefore do not read this error as “add a function until rustc is quiet.” I read it as “compare the whole implementation with the exact trait contract.”

Defaults change which items are required

A method body in the trait is a default implementation. An implementor can inherit it unless another condition makes it unavailable. A method declaration ending with a semicolon has no body and must be supplied.

The same distinction matters during API design. A default can make adding a new method less disruptive to existing implementations, but only if one behaviour is genuinely correct for all of them. A default that silently returns an empty result or ignores work can preserve compilation while breaking meaning.

I prefer an explicit required item when every implementation must make a real policy choice.

The repaired program implements the missing behaviour

The repaired fixture provides report with the same receiver and return type as the trait. It also calls the method, so the evidence proves more than an unused declaration compiling.

For a real implementation, I would test the semantic promise too. Returning "healthy" from every health check may compile while hiding an outage. Trait completeness is a type-system property; operational correctness remains my responsibility.

Trait evolution is a common source of E0046

Suppose a crate owns ten implementations of a public trait and adds one required method. All ten become incomplete immediately. Downstream crates see the same failure after updating the dependency.

My first check is the resolved version of the trait, not an example copied from another branch. Feature flags can also change which item set is compiled. Generated code may implement an older schema while the dependency exposes a newer one.

When I control the trait, I treat a new required item as a compatibility decision. Sometimes a new extension trait is cleaner than expanding the original contract.

Macro-generated impls need coverage for every shape

Derive and schema macros often emit an impl from input metadata. One arm may forget a newly required method only for enums, generic inputs, or a feature combination.

I keep small compile tests for representative shapes. A runtime test cannot start when an impl is structurally incomplete. Compile-fail fixtures are useful here because they lock both the failure and the repair near the generator.

The official E0046 explanation explicitly includes required methods, types, and constants, so a generator should inventory all three kinds.

Stub implementations can be more dangerous than the error

The compiler often suggests adding missing items. An editor may generate todo!() bodies. This makes type checking pass, but the program will panic if the path is reached.

I use a temporary stub only while the branch clearly remains incomplete. Before merging, I search for todo!, unimplemented!, placeholder constants, and impossible type aliases. E0046 is safer than a false implementation because the failure remains visible.

If the behaviour cannot yet be implemented, I may remove the impl, narrow the trait, or return a typed unsupported-operation error when that is part of the contract.

My E0046 checklist

  • Which exact trait version is rustc implementing?
  • Which required items are listed after missing:?
  • Does a feature flag or macro change the declaration?
  • Is the item a method, type, constant, or associated function?
  • Can a correct universal default exist in the trait?
  • Would adding a new required item break downstream implementations?
  • Does the repaired method preserve the semantic promise?
  • Did a generated stub merely move failure to runtime?

The core principle is simple: impl Trait for Type is a full promise, not a label. Rust accepts it only when every required part of that promise has a concrete implementation.