Returning a type based on condition


#1

What’s the idiomatic way to return a different type based on a condition inside a function? Essentially a version of this that runs:

 fn foobar<A>(key: usize) -> A {
      match key {
          1 => Foo::new(),
          2 => Bar::new(),
          _ => "Something Else"
}}

Reading other questions leads me to think it involves wrapping it in a result, custom or otherwise, but trying this leads to issues

 fn foobar<A>(key: usize) -> Result<A, String> {
      match key {
          1 => Foo::new(),
          2 => Bar::new(),
          _ => "Something Else".to_string(),
}}

Errors being unable to infer type of let x = foobar(3) or Expected Type parameter, found struct


#2

Something like this?

struct Foo;
struct Bar;

enum A {
    AF(Foo),
    AB(Bar),
    AS(&'static str)
}

fn foobar(key: usize) -> A {
    match key {
        1 => A::AF(Foo),
        2 => A::AB(Bar),
        _ => A::AS("Something Else")
    }
}

fn main() {}

Note: Rust is a mostly statically typed language.


#3

As far as I can tell this just shifts the problem to when I have to write an unwrap() for the enum.


#4

There’s also the Any type. But first you have to explain what you’re trying to do, and why. And to keep in mind that Rust is statically typed.


#5

It’s still the way to go if you want to return multiple types, unless they share a common trait. Then you can box them as such a trait object. Let’s say Foo, Bar and &'a str implements the trait MyTrait. You can then do something like this:

fn foobar(key: usize) -> Box<MyTrait> {
    match key {
          1 => Box::new(Foo::new()), //may need to add `as Box<MyTrait>` if it isn't coerced
          2 => Box::new(Bar::new()),
          _ => Box::new("Something Else")
    }
}

This has the consequences that they will be allocated on the heap, which is a bit costly, and you will be unable to distinguish between them unless MyTrait is implemented like Any.

Edit: or just use the Any trait directly, as @leonardo said, but that will prevent you from using the values for anything unless you unwrap them.


#6

Exactly, that is the most important question.


#7

I think I need to rethink the model - thinking too pythony.

Essentially I’m parsing a file type, the header of which has two different versions (of different sizes), using #[repr(C)] structs atm. I think I’ll probably rewrite it to read into a version-agnostic Header type instead.