Skip to content

You can instantiate a dyn Trait that doesn't implement Trait #88904

Open

Description

I tried this code:

pub trait Marker<'a> {}

pub trait Trait where for<'a> &'a Self: Marker<'a> {
    fn usable(&self) {}
}

impl<'a> Marker<'a> for &'a () {}
impl Trait for () {}

pub fn f() {
    // `(): Trait`
    let u = ();
    u.usable();

    // `dyn Trait` exists and `()` can coerce to it...
    let _t: &dyn Trait = &();
    // But `dyn Trait: !Trait`
    // _t.usable();
    // error[E0277]: the trait bound `for<'a> &'a T: Marker<'a>` is not satisfied
}

I expected to see this happen: The unsized coercion is denied (but see discussion below).

Instead, this happened: The unsized coercion happened and we end up with a dyn Trait which does not implement Trait.

Meta

This works on any stable version of Rust.

Notes

The failure to meet the trait bound is caught when you actually try to use the dyn Trait, at least trivially. I haven't found a way to weaponize this, but I haven't put any effort into doing so either.

dyn Trait implements Trait if you impl<'a> Marker<'a> for &'a (dyn Trait + 'static) {} in the example. However, the non-super-trait bound need not be a marker trait (it can have non-defaulted methods).

Related: Implied Bounds, #20671

Discussion

I'm filing this as a bug because the assertion that dyn Trait: Trait always holds has been expressed as a belief by Rust team members, e.g.

There are some possibilities in line with this assertion:

  • Unsized coercion checks the bounds and is denied in this case
  • Bounds that aren't super-traits make a trait object unsafe generally
  • Bounds that aren't super-traits don't apply to dyn Trait

However, another possibility is to generalize RFC 2027 and just accept the current behavior:

  • dyn Trait is always a valid type, as per RFC 2027
  • Unsized coercion is still dependent on all methods being either object safe or not applicable (where Self: Sized)
    • Though this could be relaxed too (if you're not object safe, dyn Trait just doesn't implement Trait)
  • If unsized coercion is possible, you can instantiate a dyn Trait, even if it doesn't implement Trait
  • Naturally, you can't use the Trait methods if it doesn't implement Trait

I'm sure there are a lot of subtleties and backwards-compatibility concerns in any of the possible approaches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-trait-objectsArea: trait objects, vtable layoutArea: trait objects, vtable layoutC-bugCategory: This is a bug.Category: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.Relevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions