Why are these implementations conflicting?!

I am trying to understand how frameworks like actix or axum can accept as function callbacks other functions with a variable number of parameters, for example here: Extractors .
In the source code of axum I found this macro: axum/mod.rs at be68227d739aece4210d4e7cb7d419da4d4afe30 · tokio-rs/axum · GitHub
but I am not able to replicate its behaviour.

Here my attempt:


pub trait RestType<I, O> {}
pub trait IntoResponse{}

pub trait FromRequest<T>{}

pub trait Handler<I, O, B, T> {
    fn route<REST: RestType<I, O>>(self, rest: &REST);
}

impl <I: Serialize + DeserializeOwned + Send + 'static, O: Serialize + DeserializeOwned + Send + 'static, B, F, R, E, P1> Handler<I, O, B, (P1)> for F
where 
    F: 'static + Send + Sync + FnOnce(I, P1) -> R,
    R: Future<Output = Result<O, E>> + Send,
    E: IntoResponse + Send + 'static,
    B: Send + 'static,
    P1: FromRequest<B>
{
    fn route<REST: RestType<I, O>>(self, rest: &REST) {
        todo!()
    }
}

impl <I: Serialize + DeserializeOwned + Send + 'static, O: Serialize + DeserializeOwned + Send + 'static, B, F, R, E, P1, P2> Handler<I, O, B, (P1, P2)> for F
where 
    F: 'static + Send + Sync + FnOnce(I, P1) -> R,
    R: Future<Output = Result<O, E>> + Send,
    E: IntoResponse + Send + 'static,
    B: Send + 'static,
    P1: FromRequest<B> + Send,
    P2: FromRequest<B> + Send,
{
    fn route<REST: RestType<I, O>>(self, rest: &REST) {
        todo!()
    }
}

This code does not compile because of conflicting implementations; anyway, I cannot understand what I am doing wrong.
The part less clear to me is that it does compile if I remove the T generic from the FromRequest<T> trait.
Could anyone help me understand it?

You need a comma in (P1) otherwise it's not a tuple, and is the same as just P1. I.e. do (P1,) in your first impl.

- Handler<I, O, B, (P1)> for F
+ Handler<I, O, B, (P1,)> for F
1 Like

what!?!? Really?! I can't believe it :flushed:
You are right, it works now!
But then, why does it work if I remove the generic T from the FromRequest<T>?

Because there's no inplementation of FromRequest for tuples, and FromRequest is a local trait. When it was generic, someone else downstream could implement it for a tuple using their own local type as a parameter (at least, Rust thinks this is the case. I don't remember if this is actually possible). But now that option is gone, so Rust knows that tuples don't implement FromRequest. Add FromRequest for (T, U) and you'll see the conflict again

1 Like

@RustyYato
thank you. I would have never been able to solve this one. And even now... I am not sure I completely understood the whole picture :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.