Returning different types in a match

Is there a way to return different types each arm of a match block?

I want to do

match foo {
   bar => Foo<u32> { ...},  
  baz => Foo<something_else> {...}
...
}

but this won't work because it isn't valid syntax. I am returning the same structure but on different types.

Is there a better way to do this? (even if i have to rewrite it no issues)

EDIT: The types i want to return all implement the same trait.

Thanks

1 Like

Are you thinking of using the "impl Trait" feature? If so, I'm afraid this won't work. The RFC specifies:

If a function returns impl Trait, its body can return values of any type that implements Trait, but all return values need to be of the same type.

So if your function has multiple paths that return a value, each path must return the same type.

Edit: citation

Yeah I did see the feature which i guess is in the nightly build. I am on the stable build (1.22.1) and I was trying to find a different way to implement this functionality. Guess it isn't possible then.

How can impl trait solve your problem?

Not sure yet. I will have to dig in and try some code and see. Any concerns?

impl Trait won’t help here. You can either return the type in a Box or create a custom enum that holds the different types (assuming you can name them).

4 Likes

Thanks. Well, the types are a structure that implement a certain trait. So I am wondering if there is a better way to solve this. Also how does box-ing it solve the issue?

Boxing allows you to return a trait object, which has its type erased - that allows you to return different underlying types across your match arms - caller just sees a trait object.

5 Likes

Yeah just read through the docs on Trait Objects again and looks like it is the thing what I wanted. Thanks :slight_smile:

1 Like

Trait objects have some overhead though. You can make a cheaper implementation if you return a enum:

#[derive(Debug)]
enum MyOutput {
    Var1(Vec<i32>),
    Var2(Vec<String>),
}

fn func1(i: i32) -> MyOutput {
    match i {
        1 => MyOutput::Var1(vec![1, 2, 3]),
        _ => MyOutput::Var2(vec!["a".into(), "b".into()]),
    }
}

Playground

(Replace Var1 and Var2 with meaningful names if possible)

You can implement some traits or helper methods on MyOutput to make using the output easier.

1 Like

Thanks. But in this case i cant use an enum because as this grows i will have a lot of types (structs).

I am thinking of rewriting the entire thing by implementing the trait on the enum directly. Is this present in the stable build? Cause when I searched online i found an RFC of September.

I think trait objects are the simplest solution, depending on your use case you may have to Box or just use a reference, as in:

let a: &Debug = 
    match x {
        a => &"hi",
        b => &3
     };
println!("{:?}", a);

You mean each enum variant being its own type? If so, that’s not implemented anywhere AFAIK.

I mean if you have

Enum Foo {
Bar, 
Baz,
Faz
}

i was wondering if I could do:

impl SomeTrait for Foo::Bar {...}
impl SomeTrait for Foo::Baz {...}

Right, you can’t do that.

What would the meaning of this code be? Foo::Bar is not a type, so implementing a trait on it makes no sense. You could implement a trait on the enum itself. If payload for each enum variant implements a trait, you can implement that trait on the enum like this:

enum Foo {
  Bar(Payload1),
  Baz(Payload2),
}
// Payload1 and Payload2 implement SomeTrait

impl SomeTrait for Foo {
  fn someFunc(&self) {
    match *self {
      Foo::Bar(ref data) => data.someFunc(),
      Foo::Baz(ref data) => data.someFunc(),
    }
  }
}

Yeah thanks for clarification.

I am hitting this problem too, and from reading the above it seems returning "impl Trait" might be an answer although it's a newer feature?

Specifically my use case is matching over strings and then returning different types which all have a particular Trait - something like a switch/return in other languages, but with the constraint that they all share a specific Trait.

What would be the best way to go about that these days? I'd prefer not to have to write new enums to handle this, if possible...

Well, then you'd go for a trait object, i.e., dyn Trait, or in your case it'd probably be best to have a Box<dyn Trait> because trait objects are !Sized