'impl trait' for destructuring a variant / enum

Hi, I have a variant/enum that can hold only types that implement a specific trait C. I want to work with the value within over the trait. I know that this can achieved with as &dyn C. However, I'd prefer to transfer the ownership of the data within the enum to a new variable with the help of -> impl C. How can this be achieved?

pub struct A;
pub struct B;

pub trait C {}

impl C for A {}
impl C for B {}

pub enum Foo {
    A(A),
    B(B),
}


impl Foo {

    // this works
    pub fn get(&self) -> &dyn C {
        match self {
            Foo::A(x) => x as &dyn C,
            Foo::B(x) => x as &dyn C,
        }
    } 

    // I hoped this would work but leads to an unclear error
    //  `match` arms have incompatible types
    pub fn extract(self) -> impl C {
        let x = match self {
            Foo::A(x) => x,
            Foo::B(x) => x,
        };
    } 

    // does not work neither
    pub fn extract2(self) -> impl C {
        let x = match self {
            Foo::A(ref x) => x as & dyn C,
            Foo::B(ref x) => x as & dyn C,
        };
        *x
    } 
}

can this be done at all?

No, what you want is impossible using impl Trait. The impl Trait placeholder doesn't provide dynamic dispatch or type erasure. It always stands in for a single, concrete type. It's not much different from a concrete return type except that the compiler intentionally hides the specific type from the caller, only allowing it to use methods defined by the specified trait(s). But the return value is always returned by-value, and is known to the compiler. Therefore you can't return two different concrete types as an impl Trait.

If you want to return a dynamically-chosen member of a set of concrete types by value, use Box<dyn Trait>.

You can use auto_enums to create an enum variant around each of your concrete return types, and it dispatches its trait implementation accordingly.

In the context of your example, that's not much different than writing impl C for Foo in the first place, except you can keep it hidden from your public API.

Thanks for your answer! I want to avoid the head allocation (and copying) of the data. ... The &dyn C reference will do the trick. The only problem with that solution is that need an explicit variable holding the enum so that the lifetime of the reference can be resolved.

In the real-world example, the trait C has many methods. If I understand auto_enums correctly, I'd have to implement C and delegate to the variants, correct?

Yeah - AIUI you can use a proc-macro derive with auto_enums, but I haven't tried that.

The copying would occur just as much with by-value returning. Moving stuff into a Box doesn't create deep copies of internally-owned buffers transitively. It's exactly like moving the same value to any other place.

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.