Unconstrained Type Parameter with `tk-http` Service Associated Types

Hi, I'm trying to implement a tk_http that needs to be a little more involved than the simple notion of Fn(Request) -> Response, but am failing to satisfy the type checker when implementing the tk_http::server::buffered::Service trait. The following code does not compile:

impl<D, F> Service<D> for MockNetworkService
    where F: Future<Item=EncoderDone<D>, Error=Error>
{
    type Future = F;
    // not for use with websockets yet - just ignore it
    type WebsocketFuture = future::FutureResult<(), ()>;

    // implement http body echo as a mock
    fn call(&mut self, request: Request, mut encoder: Encoder<D>) -> F {
        // mock as if this request will cause the service to be unavailable for the next second
        self._availability_stream.push(future::ok(false));
        // echo the body
        encoder.write_body(request.body).unwrap();
        // ensure that the content size header is correct
        encoder.add_length(request.body.len() as u64).unwrap();

        // timer representing this work being completed 1 second in the future
        let work_timer = Timer::sleep(Duration::new(1, 0));
        // when work_timer yields, let the availability stream know it's available again
        self._availability_stream.push(work_timer.map(|_| true));
        // also when work_timer yields, return the encoded result
        work_timer.map(|_| encoder.done())
    }
}

It fails with the error:

error[E0207]: the type parameter `F` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:59:9

Any help would be greatly appreciated - thanks!

Hi, it looks like the actual type that call is returning is not "F", it's some specific future type. I think that in this case, it would be logical use use a boxed future type for Future. This solves the problem by removing the F parameter and should solve the type checking issues with the call method.

Does the service trait have any examples that shows you how to do this?

The code doesn't return a future at all - it looks to be using the normal Timer. You probably want to use tokio_core::reactor::Timeout - Rust rather than the timer. In that case, you'd drop the F type parameter and instead specify the type Future associated item as either a boxed future or the exact future type (if you're mapping, it'll probably be something like Map<Timeout<...>, EncoderDone<...>).

Another way to describe the original code is as if you did the following:

fn bar<T>() -> T {
    5i32 // return i32
}

The function is generic and says the caller picks the concrete type T and the function returns it. But instead we return our own type, an i32.

I'm pretty sure that the partially-specified type of Future in this example should be as follows:

use futures::{future, Future};
use tokio_timer::Sleep;
use tk_http::server::EncoderDone;

type Future = future::Map<Sleep, FnOnce(<Sleep as Future>::Item) -> EncoderDone<D>>;

However trying to specify the type to this degree brings its own complications as the FnOnce(<Sleep as Future>::Item) -> EncoderDone<D> part does not implement Sized, and future::Map complains as a result.

@bluss The closest thing to an example that I could find was the generic implementation for any function that takes a Request, Encoder, and produces a future EncoderDone (from the source docs). All the official library examples generally only use this implementation:

impl<S, T, F> Service<S> for T
    where T: Fn(Request, Encoder<S>) -> F,
        F: Future<Item=EncoderDone<S>, Error=Error>,
{
    type Future = F;
    type WebsocketFuture = FutureResult<(), ()>;
    fn call(&mut self, request: Request, encoder: Encoder<S>) -> F
    {
        (self)(request, encoder)
    }
    fn start_websocket(&mut self, _output: WriteFramed<S, WebsocketCodec>,
                                  _input: ReadFramed<S, WebsocketCodec>)
        -> Self::WebsocketFuture
    {
        /// Basically no websockets
        Ok(()).into_future()
    }
}

@vitalyd Thank you for suggesting to use the timout future that comes with tokio_core. I must have overlooked it while looking for something of the same functionality. Would it be possible to box the closure used by future::Map to satisfy the type-checker?

Thanks again

Do you mean to box the Map itself and return a Box<Future<...>> in the signature? Or something else?

I was thinking boxing the closure at the end ie

type Future = future::Map<Sleep, Box<FnOnce(<Sleep as Future>::Item) -> EncoderDone<D>>>;
...
work_timer.map(Box::new(|_| encoder.done()))

But I'm having trouble getting the types to check out

I think you should just box the whole thing (ie Box::new(work_timer.map(...))) and return a Box<Future<Item=EncoderDone<D>, Error=...> if you don't mind the heap allocation and virtual dispatch.

1 Like

Thanks - that worked!

I'm kinda sad that we weren't able to get it working without heap allocation but at least the ball is rolling!

Cheers

Indeed, the allocation is unfortunate. Hopefully impl Trait will make this feasible without allocating: Map<Sleep, impl FnOnce ...>

1 Like