Skip to content

trait alias dyn type equality doesn't work #55629

Closed

Description

@nikomatsakis

If you have trait Foo { } and trait Bar = Foo, we do not currently consider dyn Foo and dyn Bar to be the same type.

Subissue of #55628

Activity

  1. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Nov 2, 2018
  2. alexreg commented on Nov 2, 2018

    @alexreg
    Contributor

    Here's an example from my comment in #55101:

    #![feature(optin_builtin_traits)]
    #![feature(trait_alias)]
    
    trait A<T: Send> {}
    trait B<T: Send> = A<T>;
    
    struct Foo<T>(T);
    struct Bar();
    
    impl<T: Send> A<T> for Foo<T> {}
    
    impl !Send for Bar {}
    
    fn main() {
        let b: Box<B<Bar>> = Box::new(Foo(Bar()));
        let a: Box<A<Bar>> = b; // should work
    }

    yields

    error[E0308]: mismatched types
      --> foo.rs:16:26
       |
    16 |     let a: Box<A<Bar>> = b;
       |                          ^ expected trait `A`, found trait `B`
       |
       = note: expected type `std::boxed::Box<dyn A<Bar>>`
                  found type `std::boxed::Box<dyn B<Bar>>`
    

    Whereas references to A and B should clearly be interchangeable.

  3. alexreg commented on Nov 2, 2018

    @alexreg
    Contributor

    As @nikomatsakis said on Zulip, this is sort of a special case of upcasting. In general, if trait A is a subtype of trait B (i.e. A's bounds include all of B's bounds and possibly more), then dyn A should be convertible to dyn B.

  4. alexreg commented on Dec 5, 2018

    @alexreg
    Contributor

    Note a seemingly surprising consequence on coherence that this has:

    Playground

    #![feature(trait_alias)]
    
    trait Foo {
        fn foo(&self) -> u32;
    }
    
    trait SendAlias = Send;
    
    impl Foo for dyn Send {
        fn foo(&self) -> u32 {
            1
        }
    }
    
    impl Foo for dyn SendAlias {
        fn foo(&self) -> u32 {
            2
        }
    }
    
    fn main() {
        let x1 = Box::new(()) as Box<Send>;
        let x2 = Box::new(()) as Box<SendAlias>;
        println!("{}", x1.foo());
        println!("{}", x2.foo());
    }

    This is correct behaviour for now, and it doesn't cause any ICEs, but obviously is not what one would expect intuitively.

  5. maltuned commented on Apr 4, 2026

    @maltuned

    Has this issue been fixed now?

    #![feature(trait_alias)]
    
    trait A {}
    
    trait B = A;
    
    struct S;
    
    impl A for S {}
    
    fn f(_: &dyn B) {}
    
    fn main() {
        let s = S;
        f(&s);
    }

    The above code passes compilation on rustc 1.95.0-nightly (efc9e1b50 2026-02-06).

  6. fmease commented on Oct 22, 2026

    @fmease
    Member

    Has this issue been fixed now?

    Yep, nowadays we expand trait aliases "away" "eagerly" inside trait object types. Whether that behavior will stay is a separate question.

  7. added
    A-dyn-traitArea: trait objects, vtable layout
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Oct 22, 2026
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 layoutA-trait-systemArea: Trait systemC-bugCategory: This is a bug.F-trait_alias`#![feature(trait_alias)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.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

      Issue actions