2
votes

I am trying out Rust and ❤️ it so far.

But currently I am stuck with generic traits :)

Status Quo:

There is this trait I want to implement, which I cannot modify:

pub trait Handler<R, B, E> {
    fn run(&mut self, event: http::Request<B>) -> Result<R, E>;
}

One implementation of that trait in the same library is:

impl<Function, R, B, E> Handler<R, B, E> for Function
where
    Function: FnMut(http::Request<B>) -> Result<R, E>,
{
    fn run(&mut self, event: http::Request<B>) -> Result<R, E> {
        (*self)(event)
    }
}

And this implementation can be used as follows:

fn handler(req: http::Request<Body>) -> Result<impl IntoResponse, MyError> {
    ...
}

With the IntoReponse trait:

pub trait IntoResponse {
    fn into_response(self) -> Response<Body>;
}

What I want to do:

I want to implement that trait for a struct to be able to be used with the types mentioned above.

I tried:

impl Handler<impl IntoResponse, Body, MyError> for GQLHandler {
    fn run(&mut self, req: http::Request<Body>) -> Result<impl IntoResponse, MyError> {
        ...
    }
}

But that results in the errors:

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> handlers/gql.rs:18:14
   |
18 | impl Handler<impl IntoResponse, Body, NowError> for GQLHandler {
   |              ^^^^^^^^^^^^^^^^^

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> handlers/gql.rs:19:59
   |
19 |     fn run(&mut self, req: http::Request<Body>) -> Result<impl IntoResponse, NowError> {
   |                                                           ^^^^^^^^^^^^^^^^^

It works of cause if I implement it for a specific type, e.g.

impl Handler<http::Response<Body>, Body, NowError> for GQLHandler {
    fn run(&mut self, req: http::Request<Body>) -> Result<http::Response<Body>, NowError> {

but I would like to keep the impl Trait somehow.

Looking forward to any suggestions.

Thanks & Cheers Thomas

EDIT:

Following up on @MaxV's answer (Thanks!), sadly that did not work for me (which is why I did not accept this answer yet).

See this playground

When trying to return Ok(...) with a type implementing IntoResponse, I get the following error:

  |
3 | impl<T: IntoResponse> Handler<T, Body, MyError> for GQLHandler {
  |      - this type parameter
4 |     fn run(&mut self, req: Request<Body>) -> Result<T, MyError> {
5 |         Ok(Response::<()>::new(()))
  |            ^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `http::Response`
  |
  = note: expected type parameter `T`
                     found struct `http::Response<()>`

even though I implemented IntoResponse for Response:

trait IntoResponse{
    fn foo(&self);
}

impl IntoResponse for Response<()>
{
    fn foo(&self) {}
}

What am I missing?

1

1 Answers

0
votes

New answer

It feels like you're looking for Existential types:

Right now, it isn't possible to return an impl Trait type from a trait implementation. This is a huge restriction which this RFC fixes <...>

Existential types RFC was merged but the implementation doesn't stabilized. You can track the progress there.

Original answer

You can't use impl there but you can solve it this way:

Playground

impl<T: IntoResponse> Handler<T, Body, MyError> for GQLHandler {
    fn run(&mut self, req: http::Request<Body>) -> Result<T, MyError> {
        // ...
    }
}