2

I'd like to be able to destructure/upcast enum variants to a commonly implemented trait. Here's the setup:

trait SomeTrait {
    fn some_func(&self);
}

struct SomeStruct1;
impl SomeTrait for SomeStruct1 {
    fn some_func(&self) {}
}

struct SomeStruct2;
impl SomeTrait for SomeStruct2 {
    fn some_func(&self) {}
}

enum SomeEnum {
    Value1(SomeStruct1),
    Value2(SomeStruct2),
}

Here's one possibility I tried:

fn call_some_func(some_enum: SomeEnum) {
    match some_enum {
        SomeEnum::Value1(ref some_trait: &SomeTrait) |
        SomeEnum::Value2(ref some_trait: &SomeTrait) => some_trait.some_func()
    }
}

It resulted in this error:

error: expected one of `)`, `,`, or `@`, found `:`
  --> src/main.rs:22:40
   |
22 |         SomeEnum::Value1(ref some_trait: &SomeTrait) |
   |                                        ^ expected one of `)`, `,`, or `@` here

This was another idea I had:

fn call_some_func2(some_enum: SomeEnum) {
    match some_enum {
        _(ref some_trait: &SomeTrait) => some_trait.some_func()
    }
}

Which also failed:

error: expected one of `=>`, `if`, or `|`, found `(`
  --> src/main.rs:22:10
   |
22 |         _(ref some_trait: &SomeTrait) => some_trait.some_func()
   |          ^ expected one of `=>`, `if`, or `|` her

Does anyone know of a way to accomplish this type of destructuring?

Ryan C.
  • 87
  • 1
  • 6
  • Will every case of the enum contain something that implements the trait, like you've shown here, or will there be some cases which don't? – Dan Hulme May 08 '18 at 17:06
  • For my current usage, yes they all implement the trait. I wouldn't be opposed to a solution that allowed pattern matching so that wasn't a requirement though. – Ryan C. May 08 '18 at 17:14

1 Answers1

4

No, you cannot while destructuring. You can do it after though:

// My preferred
fn call_some_func(some_enum: SomeEnum) {
    let x: &SomeTrait = match some_enum {
        SomeEnum::Value1(ref x) => x,
        SomeEnum::Value2(ref x) => x,
    };
    x.some_func();
}
// Also works
fn call_some_func(some_enum: SomeEnum) {
    let x = match some_enum {
        SomeEnum::Value1(ref x) => x as &SomeTrait,
        SomeEnum::Value2(ref x) => x,
    };
    x.some_func();
}

I'd encourage you to extract this to a method for reuse:

impl SomeEnum {
    fn as_trait(&self) -> &SomeTrait {
        match *self {
            SomeEnum::Value1(ref x) => x,
            SomeEnum::Value2(ref x) => x,
        }
    }
}

fn call_some_func(some_enum: SomeEnum) {
    some_enum.as_trait().some_func();
}

And probably to implement the trait on the enum itself, if all the variants implement it. This way, consumers don't have to care:

impl SomeTrait for SomeEnum {
    fn some_func(&self) {
        self.as_trait().some_func()
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366