Getting static dispatch with impl Trait

Given the following code in another library:

pub fn foo() -> impl Display {
  SomeExoticType1
}

pub fn bar() -> impl Display {
  SomeExoticType2
}

how do I do static dispatch?

pub fn my_func() -> impl Display {
  if true {
    foo()
  } else {
    bar()
  }
}

My normal approach would be to use an enum, but I can't because I can't name the types.

enums work: playground

But a question, why does that work? The return is Either<impl Display, impl Display> which shouldn't work with the impl Deref for Either as it's impl Display and not &impl Display and there isn't an impl like this:

impl<T> Deref for T 
where &T: Deref<Target=T>

(Which would be kind of illogical...)

I hope one day we will get a language-level support for use-cases like this, but until then try auto_enum crate.

The return value of my_func is generally Either<impl Display, impl Display>, and in this particular case, Either<i32, usize> since both types satisfy the impl Display requirement. All Either's deref target requires is that the type implements the Display trait. I'm not sure where you're getting &impl Display from.

Well it's because I presumed that because neither usize nor i32 implement deref, that it wouldn't satisfy the Deref impl on Either
I mentioned the &impl Display because that would satisfy the constraints, &T: Deref<Target=T>

This is not going through Deref, but rather Either<L: Display, R: Display>: Display: either::Either - Rust

2 Likes

Oh, I was totally misreading. @vitalyd's answer makes sense.

1 Like

Sadly I'm not actually using Display, I'm using rocket::response::Responder, which doesn't have an implementation for Either. I've ended up doing

#[derive(Debug)]
enum Either<A, B> {
    Left(A),
    Right(B),
}

impl<'r, A, B> Responder<'r> for Either<A, B>
where
    A: Responder<'r>,
    B: Responder<'r>,
{
    fn respond_to(self, request: &rocket::request::Request) -> rocket::response::Result<'r> {
        match self {
            Either::Left(a) => a.respond_to(request),
            Either::Right(b) => b.respond_to(request),
        }
    }
}

just feels like boilerplate.