Why Box<Future<...>> is type equivalent to just Future<...>


#1

I am trying to breakdown code sample from Hyper for the server (see below code sample). It defines call function on HelloService, which returns Box<Future<...>>. I understand that currently futures need to be returned boxed until impl Future feature for the return type is not stabilized in the compiler. So, it is clear why the code sample suggest return type to be boxed future:

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

use futures::future::Future;

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

struct HelloWorld;

const PHRASE: &'static str = "Hello, World!";

impl Service for HelloWorld {
    // boilerplate hooking up hyper's server types
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    // The future representing the eventual Response your call will
    // resolve to. This can change to whatever Future you need.
    type Future = Box<Future<Item=Self::Response, Error=Self::Error>>; // <==== here it is boxed

    fn call(&self, _req: Request) -> Self::Future {
        // We're currently ignoring the Request
        // And returning an 'ok' Future, which means it's ready
        // immediately, and build a Response with the 'PHRASE' body.
        Box::new(futures::future::ok(
            Response::new()
                .with_header(ContentLength(PHRASE.len() as u64))
                .with_body(PHRASE)
        ))
    }
}

However, the original parent Service trait defines the call function without boxed future:

    /// The future response value.
    type Future: Future<Item = Self::Response, Error = Self::Error>; // <===== but here it is not boxed

    /// Process the request and return the response asynchronously.
    fn call(&self, req: Self::Request) -> Self::Future;

How can the signature of the implementation HelloService::call with different return type satisfy the requirement of the being implemented interface Service::call? The compiler is happy about it. Could you please explain what the magic happens here?


Rust Objectives Observation
#2

The futures crate has

impl<F: ?Sized + Future> Future for Box<F>
  type Item = F::Item;
  type Error = F::Error;

So a boxed future is also a future.


#3

Thank you! I got confused by the same issue again (one more was here: What does || mean?). Is there any way to locate various definitions of ‘implicit type conversions’ like this? How do you find them? Just know the API? read the documentation? or some IDE can help? or maybe there is a golden strategy on how to break through questions like this?


#4

Well, I wouldn’t call them implicit but I get what you’re saying.

What I do is go look at the docs for the trait I’m interested in. In this case, it’s https://docs.rs/futures/0.1.16/futures/future/trait.Future.html. Then I look at the implementation section to see what impls are available - the box one is there.

Box is also such a common wrapper that there will usually be “delegating” impls for it (eg Read and Write).

For hyper’s Service/NewService:
https://hyper.rs/hyper/master/hyper/server/trait.NewService.html
https://hyper.rs/hyper/master/hyper/server/trait.Service.html

Scroll down to the implementers section as well.


#5

Thanks. Now I understand that keeping documentation side by side (or remembering it) with the code is vital in Rust, which is unfortunate, IMO. Basically, in order to understand what is going on, it is not enough to have IDE with complete indexing and navigation to all callees/callers :frowning: I am coming from Java/Scala background, where IDE based assistance to navigate through all callers/callees is so strong that you almost never use the documentation (what I think is good).


#6

The IDE story (and tooling in general) is rapidly evolving so I wouldn’t get discouraged just yet. You’ll also get more familiar with the lay of the land after you get more mileage under your belt. The rust docs are searchable so the burden of keeping them open isn’t huge IMO (of course “find all implementers” inside the IDE would be good for this). Java IDE support is one of the best across all languages (the language is a lot simpler though) so it’s a high bar but a good one to shoot for :slight_smile: (Scala support I’ve not heard great things about but maybe that’s changed recently).

But mostly, I think you’ll get more settled in with some experience.


#7

Thank you. I certainly will. Here is the ticket on Intelij plugin: https://github.com/intellij-rust/intellij-rust/issues/2037

PS: Intelij Scala plugin is great one. At least I did not have a problem to find using only IDE what is being actually called here and there.