E0021 — dyn Trait object-safety violation

A trait used as dyn Trait declares a method that can’t be dispatched through an erased trait-object receiver.


What triggers it

Two shapes are rejected:

  • A method with no self receiver — there’s no erased receiver value for a dyn Trait call to dispatch through.
  • A method that returns Self — a dyn Trait caller has, by definition, erased the concrete type, so a method promising to hand back “the caller’s own concrete type” can’t be satisfied.

The check fires wherever dyn Trait appears — a fn parameter, fn return type, let binding, or struct field — as soon as Trait names a method with either shape, regardless of whether that specific method is ever called through the trait object.

Minimal example

trait Factory {
    fn make() -> int;
}

fn use_factory(dyn Factory f) -> int {
    return 0;
}

Output:

scratch.rz:5:1: [E0021] `dyn Factory` is not object-safe: method `make` has no `self` receiver, so it cannot be dispatched through a `dyn Trait` value
trait Cloneable {
    fn duplicate(self) -> Self;
}

fn use_cloneable(dyn Cloneable c) -> int {
    return 0;
}

Output:

scratch.rz:5:1: [E0021] `dyn Cloneable` is not object-safe: method `duplicate` returns `Self`, which a `dyn Trait` caller cannot be given (the concrete type is erased)

Fix

Split the trait: keep the object-safe methods (those with a self receiver and no Self-typed return) on the trait used as dyn Trait, and move the non-object-safe methods to a separate trait or an inherent method on the concrete type, called only where the concrete type is statically known.

Source

Emitted from resilient/src/dyn_trait.rs’s object-safety pass, part of the dyn Trait v2 work (issue #4095, increment 1). Vtable codegen, dyn Trait in generic/container position, and flow-sensitive coercion checking are tracked as follow-up increments on the same issue.