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
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<...>).
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?
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.