Rustc bug?: generic T is also Box<dyn T>


#1

The following code fails to compile:

pub trait ATrait {}

impl<T: ATrait + ?Sized> ATrait for Box<T> {}

impl<'a, T: ATrait + 'a> From<T> for Box<dyn ATrait + 'a> {
    fn from(t: T) -> Box<dyn ATrait + 'a> {
        Box::new(t)
    }
}

with the error message

error[E0119]: conflicting implementations of trait `std::convert::From<std::boxed::Box<(dyn ATrait + 'static)>>` for type `std::boxed::Box<(dyn ATrait + 'static)>`:
 --> box_from_clash.rs:7:1
  |
7 | impl<T: ATrait> From<T> for Box<dyn ATrait> {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> std::convert::From<T> for T;

error: aborting due to previous error

but the from impl is converting between different types: it converts T to Box, which are definitely different. Is this a rustc bug, a rustc design choice, or a misunderstanding by me? :slight_smile:

EDIT It’s worth noting that the code will compile with no problems if you remove the ?Sized bound.


#2

The dyn Trait type is a type that implements Trait, so this seems correct to me.

Removing the ?Sized “fixes” it because dyn Trait is an unsized type.


#3

Consider the following code:

let boxed: Box<dyn ATrait> = Box::new(SomethingThatImplementsATrait);
let boxed2: Box<dyn ATrait> = boxed.from();

In the second line the compiler needs to find an implementation for <Box<dyn ATrait> as From<Box<dyn ATrait>>>::from, it can either take the fully generic one from core, or, because Box<dyn ATrait>: ATrait, the one you have written.


#4

I’m just confused because one is wrapped in a Box and the other isn’t. Does the compiler consider Box<T> and T to be the same type, or more specifically Box<dyn Trait> and T: Trait?


#5

In the from impl, what if you replace T with Box<dyn ATrait + 'a>, then you will have a conflicting impl with From<T> for T impl, so the compiler rejects your impl. This is correct and can be fixed by removing the ?Sized bounds because dyn Trait: Trait and is unsized (i.e. doesn’t impl Sized) like how @Ixrec said above. This means that it is guaranteed to that Box<dyn ATrait + 'a>: ATrait is false, which means it cannot conflict with From<T> for T due to the constraint on T (T: ATrait).


edit: code blocks for clarity

impl<U: ATrait + ?Sized> ATrait for Box<U> {}

// replace `T` with `Box<dyn ATrait + 'a>`
// this is valid because `Box<dyn ATrait + 'a>: ATrait` by the first impl
// oh no a conflict with `impl From<T> for T`
impl<'a> From<Box<dyn ATrait + 'a>> for Box<dyn ATrait + 'a> {}
impl<U: ATrait> ATrait for Box<U> {}

// replace `T` with `Box<dyn ATrait + 'a>`
// this is no longer valid because Box<dyn ATrait + 'a> doesn't implement ATrait,
// because `dyn ATrait` is not `Sized`, this means this impl doesn't even exist!
// yay no conflicts
// impl<'a> From<Box<dyn ATrait + 'a>> for Box<dyn ATrait + 'a> {}

#6

I understand now. It’s a shame that these impls overlap, because they are both bigger than their intersection. This means that I can never implement impl<U: ATrait + ?Sized> ATrait for Box<U> {} even though it seems like a desirable thing to have. :frowning:. Does specialization help with this?


#7

I just saw the context of where you’re trying this out. Damn, that’s annoying. Unfortunately I don’t know whether specialization can help currently.


#8

Specialization could help if it gets the lattice rule, but I don’t think that is currently planned.