Skip to content

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

Open
@QuineDot

Description

@QuineDot

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.

Activity

Jules-Bertholet

Jules-Bertholet commented on Dec 16, 2023

@Jules-Bertholet
Contributor

Another method for doing this:

trait Super {
    type Assoc: ?Sized;
}

trait Sub: Super<Assoc = Self> {}

fn foo<T: Sub>(a: &T) {
    let b: &dyn Sub<Assoc = T> = a; // This line compiles
    bar(b) // This line does not
}

fn bar<T: Sub + ?Sized>(a: &T) {}

@rustbot label A-trait-objects T-types

added
A-dyn-traitArea: trait objects, vtable layout
T-typesRelevant to the types team, which will review and decide on the PR/issue.
on Dec 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-dyn-traitArea: trait objects, vtable layoutC-bugCategory: This is a bug.T-typesRelevant 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

        Participants

        @ChrisDenton@fmease@rustbot@QuineDot@Jules-Bertholet

        Issue actions

          You can instantiate a dyn Trait that doesn't implement Trait · Issue #88904 · rust-lang/rust