0

I have an operation that I'd like to err out on invalid input, otherwise create one of two structs which implement a trait. I'm sure this isn't a shortcoming of the compiler but rather my poor understanding of the type system and I'm looking for some input. This is a simplified version of my code but the core issue remains.

pub trait Action { ... }

pub struct TaxonomySpotSearch { ... }
impl Action for TaxonomySpotSearch { ... }

pub struct SavedSearch { ... }
impl Action for SavedSearch { ... }

fn do_something() -> Result<i32, ArgError> {
    let foo: Box<dyn Action> = match Some(1) {
        Some(0) => Ok(Box::new(TaxonomySpotSearch::new())),
        Some(1) => Ok(Box::new(SavedSpotSearch::new())),
        _  => Err(ArgError::NoSubCommand)
    }?;

    Ok(...)
}

However, I'm getting the classic E0308 "match arms have incompatible types" error where the compiler is inferring the type of foo as the return value of the first match arm.

error[E0308]: `match` arms have incompatible types
   --> src/main.rs:127:20
    |
125 |       let foo: Box<dyn Action> = match Some(1) {
    |  ________________________________-
126 | |         Some(0) => Ok(Box::new(TaxonomySpotSearch::new())),
    | |                    --------------------------------------- this is found to be of type `Result<Box<TaxonomySpotSearch>, _>`
127 | |         Some(1) => Ok(Box::new(SavedSpotSearch::new())),
    | |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `TaxonomySpotSearch`, found struct `SavedSpotSearch`
128 | |         _  => Err(ArgError::NoSubCommand)
129 | |     }?;
    | |_____- `match` arms have incompatible types
    |
    = note: expected enum `Result<Box<TaxonomySpotSearch>, _>`
               found enum `Result<Box<SavedSpotSearch>, _>`

Am I doing something wrong or is this really a limitation of the type system? I would expect the addition of the explicit type annotation to resolve this, yet here I am.

RElliott
  • 93
  • 1
  • 8
  • 4
    Please don't post questions with `...` everywhere and unknown types. Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) that demonstrates the problem. An example that I can copy paste, compile, and it outputs exactly what you claim it does. Like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5593cafc57f26d1c31d5be2a9282755e – Finomnis Aug 27 '22 at 16:23

1 Answers1

2

The Rust compiler doesn't understand that you want them both to be Box<dyn Action>, it takes them as the real type they are.

You need to tell that to the Rust compiler explicitely:

pub trait Action {}

pub struct TaxonomySpotSearch {}
impl Action for TaxonomySpotSearch {}

pub struct SavedSearch {}
impl Action for SavedSearch {}

enum ArgError {
    NoSubCommand,
}

fn do_something() -> Result<(), ArgError> {
    let foo: Box<dyn Action> = match Some(1) {
        Some(0) => Ok(Box::new(TaxonomySpotSearch {}) as Box<dyn Action>),
        Some(1) => Ok(Box::new(SavedSearch {}) as Box<dyn Action>),
        _ => Err(ArgError::NoSubCommand),
    }?;

    Ok(())
}
Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • 1
    You can even shorten `as Box` to just `as _`. – user4815162342 Aug 27 '22 at 17:46
  • Didn't know that! This means, the type is derived from the receiving variable? – Finomnis Aug 27 '22 at 18:21
  • Yes, or the return value of the function, etc. After all, the type *is* ascribed explicitly. It's a shame that we have to type `as ` in the first place, but such is life. – user4815162342 Aug 27 '22 at 18:37
  • Nice, that resolved it! I'm curious why the type annotation didn't help the compiler here -- is there a resource where I could read more about this? – RElliott Aug 27 '22 at 20:59
  • Not really, I just knew because I stumbled across this before. I think the real reason is that if the compiler were allowed to figure it out himself it could lead to situations of ambiguity. But I'm not sure tbh – Finomnis Aug 28 '22 at 06:57