How to unpack mapped boxed future back to future


#1

Sorry for bombarding with questions. Rust turned out not that intuitive to deal with futures.

I have the the following source code sample. The first arm of the match in the call function collects the body in non-blocking way (I hope) and maps the result to the response. The second constructs the result of the future directly. I understand why the compiler complains, but have no idea how to fix it. Could you please help me?

extern crate futures;
extern crate hyper;
extern crate tokio_core;

extern crate serde;
extern crate serde_json;

use futures::future::Future;

use hyper::server::{Http, Request, Response, Service};

use futures::Stream;

use std::panic;
use std::result;
use std::error;

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

struct AgentService;

impl AgentService {
    fn get_body(&self, body: hyper::Body) -> Box<Future<Item=String, Error=<Self as hyper::client::Service>::Error>> {
        let full_body = body.concat2()
            .map(|chunk| {
                let v = chunk.to_vec();
                String::from_utf8_lossy(&v).to_string()
            });
        Box::new(full_body)
    }
}

impl Service for AgentService {
    // boilerplate hooking up hyper's server types
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;

    fn call(&self, _req: Request) -> Box<Future<Item=Self::Response, Error=Self::Error>> {
        //let result = panic::catch_unwind(|| {
            println!("Received {:?}", _req);
            let resp: Future<Item=Self::Response, Error=Self::Error> = match _req.path() {
                "/avl" if _req.method() == &hyper::Method::Get => {
                    self.get_body(_req.body())
                        .map(|r| {
                            println!("{:?}", r);
                            let deserialized: Point = serde_json::from_str(&r).unwrap();
                            println!("deserialized: {:?}", deserialized);
                            let resp = Response::new().with_status(hyper::StatusCode::Created);
                            resp
                        })
                }
                _ => {
                    let resp = Response::new().with_status(hyper::StatusCode::NotFound);
                    futures::future::ok(resp)
                }
            };
            Box::new(resp)
        //});

        //println!("Caught {:?}", result);
//        match result {
//            Ok(f) => {
//                return f;
//            }
//            Err(e) => {
//                return Box::new(futures::future::ok(Response::new().with_status(hyper::StatusCode::InternalServerError)));
//            }
//        }
    }
}

pub fn run() {
    let addr = "127.0.0.1:3000".parse().unwrap();
    let server = Http::new().bind(&addr, || Ok(AgentService)).unwrap();
    server.run().unwrap();
}

Compiler error:

error[E0308]: mismatched types
  --> src\server.rs:49:21
   |
49 | /                     self.get_body(_req.body())
50 | |                         .map(|r| {
51 | |                             println!("{:?}", r);
52 | |                             let deserialized: Point = serde_json::from_str(&r).unwrap();
...  |
55 | |                             resp
56 | |                         })
   | |__________________________^ expected trait futures::Future, found struct `futures::Map`
   |
   = note: expected type `futures::Future<Item=hyper::Response, Error=hyper::Error>`
              found type `futures::Map<std::boxed::Box<futures::Future<Item=std::string::String, Error=hyper::Error>>, [closure@src\server.rs:50:30: 56:26]>`

How can I drop the Box of the inner result?


#2

I have figured out that defining the resp value as boxed future

let resp: Box<Future<Item=Self::Response, Error=Self::Error>> = match ....

and returning boxed result from both arms of the match helps to resolve the error, but I do not understand why. Is it the way it should be fixed? Could you please explain why?


#3

Using box is the correct approach.
Future and other Traits are not data types. When you use Box or reference to trait you are telling the compiler to make a “Trait Object” data type. (There is a nightly dyn feature in testing to make this more clear.)
Rust is statically typed so can only deal with variables of a single type.
Each arm is returning a different data type. Box wraps both in the common type, so allowing either to get set to the variable.
There is implicit type inference that is converting Box<futures::Map<...>> into Box<futures::Future<...>>. Alternative to specifying the type after the variable would be to use instead Box::new(resp) as Box<Future<...>

Per the title; you don’t convert back. The Futures library has in its code a implementation of the Future trait for any type Box<Future<…>>.


#4

Thanks. I understand it is statically typed. I am more thinking in terms of type equivalence. What confused me is that box of map of future is assignable (i.e. type equivalent) to box of future, but map of future is not assignable to future, although map of future is also a future :slight_smile:


#5

Trait objects can only appear behind indirections such as Box or &. You are not allowed to cast futures::Map<…> into a Future trait object despite the presence of impl<…> Futures for futures::Map<…>. You can cast Box<futures::Map<…>> into a Box<Future> trait object, however.