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


#1

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!


#2

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?


#3

The code doesn’t return a future at all - it looks to be using the normal Timer. You probably want to use https://docs.rs/tokio-core/0.1.9/tokio_core/reactor/struct.Timeout.html 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<...>).


#4

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.


#5

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


#6

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


#7

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


#8

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.


#9

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


#10

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