Why can't I cast custom generic type(Container<T>) to type Container<dyn>

At the same time Box<T> to Box<dyn> cast works

struct Dog {
    
}

trait Pet {
    fn feed(&self);
}

impl Pet for Dog {
    fn feed(&self) {}
}

struct Container<T: ?Sized> {
    pet: Box<T>
}

fn main() {
    // compiles
    let a = Box::new(Dog{});
    let b: Box<dyn Pet> = a;
    
    //gives an error: expected trait object `dyn Pet`, found struct `Dog`
    let c = Container {
        pet: Box::new(Dog{})
    };
    let d: Container<dyn Pet> = c;
}

Playground: Rust Playground

First of all, there is no reason that for any generic type T and trait Trait, T<U: Trait> should in general be convertible to T<dyn Trait>.

Wrapping a single other item of type U is not all a generic container can do. What if there are multiple instances of the type, for example? How would you create a Vec<dyn Trait> in a reasonably cheap and efficient manner, given that its memory buffer needs to be contiguous, but the type dyn Trait is dynamically-sized?

It therefore seems logical that the ability for such a conversion should be opt-in. With that said, unsizing coercions are a somewhat magic and definitely unstable feature. If you are willing to use the nightly compiler, you can implement the CoerceUnsized trait to achieve this effect.

2 Likes

The short answer is that Box is a "magic" type that the compiler knows about.

The longer answer is that, it is allowed:

TyCtor(T) to TyCtor(U), where TyCtor(T) is one of

    &T
    &mut T
    *const T
    *mut T
    Box<T>

and where U can be obtained from T by unsized coercion.

Unsized coercion allows you to convert T to dyn U where T implemented U + Sized.
Thus, you can convert from Box<T> to Box<dyn U>.

1 Like

Because Box is special.

The thing you want to do is called unsizing coercion and is mostly a compiler built-in. For generic types, this is largely controlled by the CoerceUnsized trait. You can find an impl for Box<T> in the source.

CoerceUnsized is currently unstable, so you can't do what you want. If the dynamically sized types ever become stable, this functionality is expected to also be stabilized.

2 Likes

But what is idiomatic way to have a Vec of Container of different types?
If I have Vec<Container<dyn Pet>> and Container<Dog> I can't even add it to the vec

You don't. That's the idiomatic way.

1 Like

If you just want to add some coercions for some known traits, you can just define them explicitly

pub trait Pet {}

pub struct Dog;

impl Pet for Dog {}

pub struct Container<T: ?Sized>(Box<T>);

impl<T> Container<T> {
    pub fn new(value: T) -> Self {
        Self(Box::new(value))
    }
}

impl<'a, T: Pet + 'a> Container<T> {
    fn into_dyn_pet(self) -> Container<dyn Pet + 'a> {
        // Box's unsizing coercion does the work here, since the return type explicitly uses a trait object
        Container(self.0)
    }
}

fn main() {
    let mut pets = Vec::<Container<dyn Pet>>::new();

    pets.push(Container::new(Dog).into_dyn_pet());
}
4 Likes

You seem to be trying to simulate inheritance from object-oriented languages. That in itself is already not idiomatic in Rust.

Overall, this feels like an XY problem. Instead of giving us a toy example, ask about what you are trying to accomplish in reality, then we can tell you more easily how you should likely approach the problem.

1 Like

This does sound likely to be an unfitting Object-Oriented pattern, but, if this is done, the idiomatic way typically would be to define an enum with the possible contained types as fields on different variants.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.