Extract trait object from struct using Into/From?

Hello people, I'm having a small issue with the following code. I have this trait:

trait MyTrait {}

and this type:

struct InnerType {}

impl MyTrait for InnerType {
    // impl
}

I actually have 2 of these InnerType implementing MyTrait: I'll call them InnerTypeA and InnerTypeB.

In terms of design, in my case InnerTypeA and InnerTypeB show the behavior described by MyTrait, so impl MyTrait for InnerType makes sense.

Then I have an enum that contains the InnerTypes, such as:

enum OuterType {
    VariantA(InnerTypeA),
    VariantB(InnerTypeB),
}

This enum doesn't show the behavior described by MyTrait, but his components do,
so I'd like to have a method to extract this component rather than having impl MyTrait for OuterType.

My initial idea was to implement Into<MyTrait> for OuterType, but of course that doesn't work since MyTrait
is a trait and have no size.

Same thing for Into<dyn MyTrait> for OuterType since dyn MyTrait has no size,
unless I box it, but I'd prefer to prevent allocations as much as possible.


My questions:

  1. Is there a way to implement the From or Into traits to go from OuterType to MyTrait?
  2. In case there isn't, which is the alternative? Is there an idiom for such cases in Rust?

I was also thinking about using the same strategy as IntoIterator trait, but I don't know if and how that could work.

Thank you! <3

You can implement MyTrait for OuterType, and just match on OuterType and defer the implementation to the inner type.

trait Foo {
    fn call_foo(&self) -> i32;
}

impl Foo for InnerTypeA { ... }
impl Foo for InnerTypeB { ... }

impl Foo forr OuterType {
    fn call_foo(&self) -> i32 {
        match self {
            Self::VariantA(inner) => inner.call_foo(),
            Self::VariantB(inner) => inner.call_foo(),
        }
    }
}

For associated types, you will have to assert that all variants use the same associated types, otherwise you can't implement MyTrait

First of all, thank you for your answer!

I edited my main message, but here's the relevant part:

In any other case, that's what I would do; but in my specific case, OuterType is really not a MyTrait.

That seems like a weird requirement, what types are you actually using? You could do something like,

impl OuterType {
    fn as_my_trait(&self) -> &dyn MyTrait {
        match self {
            Self::VariantA(inner) => inner,
            Self::VariantB(inner) => inner,
        }
    }

    fn as_my_trait_mut(&mut self) -> &mut dyn MyTrait {
        match self {
            Self::VariantA(inner) => inner,
            Self::VariantB(inner) => inner,
        }
    }
}

Tried this solution, but got this error:

the trait `MyTrait` cannot be made into an object

the trait `MyTrait` cannot be made into an object

note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses
note: required because of the requirements on the impl of `std::ops::CoerceUnsized<&dyn MyTrait<Id = i64>>` for `&InnerTypeA`rustc(E0038)

In that case you can't do this. What is your trait? There may be another solution

In that case you can't do this. What is your trait? There may be another solution

My trait has a bunch of getters, basically.
Actual code:

pub trait Plan {
    type Id: PartialEq + Sized;

    fn id(&self) -> Self::Id;
    fn country(&self) -> &str;
    fn interval(&self) -> interval::Interval;
    fn product_sku(&self) -> &str;
    fn preset(&self) -> Option<&str>;
}

The other solution is to use a concrete type for this Plan (e.g. a struct) so as to make translations from/to concrete types, disregarding abstraction.

That defeats the purpose of a trait though:

A trait tells the Rust compiler about functionality a particular type has and can share with other types.

In this case, both InnerTypeA and InnerTypeB shows the same behavior, so it's natural to abstract them over a trait.

But OuterType is an aggregation of different values, one of which is a Plan.

OuterType is not a Plan.

I just need to extract whatever Plan an OuterType instance has, depending on its variant.

Hope this makes my constraint a little less weird :sweat_smile:

Is the Id associated type different for each variant, if so, then there is no way to do this. They are fundamentally different, and cannot be abstracted over. If Id is the same for each variant, then you should be able to apply the as_* functions.

This seems weird to me, but that's fine. I don't understand how this is possible.

1 Like

Is the Id associated type different for each variant, if so, then there is no way to do this. They are fundamentally different, and cannot be abstracted over.

Oh right, it makes complete sense.

I managed to make it work by removing the Id associated type.

Thank you again!