Calculating types

Hi,

A newbie question, How do I calculate the result of a type (like future, hyper server etc)? Consider this example (from the hyper documentation):

extern crate hyper;

use hyper::rt::{self, Future};
use hyper::server::conn::AddrStream;
use hyper::service::{make_service_fn, service_fn_ok};
use hyper::{Body, Request, Response, Server};

fn main() {
  let addr = ([127, 0, 0, 1], 3000).into();

  let make_svc = make_service_fn(|socket: &AddrStream| {
    let remote_addr = socket.remote_addr();
    service_fn_ok(move |_: Request<Body>| {
      Response::new(Body::from(format!("Hello, {}", remote_addr)))
    })
  });

  // Then bind and serve...
  let server = Server::bind(&addr).serve(make_svc);

  // Finally, spawn `server` onto an Executor...
  rt::run(server.map_err(|e| {
    eprintln!("server error: {}", e);
  }));
}

Say I want to extract a function that does all the calculations and returns the server `let server = Server..." to be ran elsewhere. What would be the return type of the function?

The same goes for extracting the make_svc to separate function, what would be the return type (as MakeServiceFn is not exported)?

Thanks for the patience :slight_smile:

Say I want to extract a function that does all the calculations and returns the server `let server = Server..." to be ran elsewhere. What would be the return type of the function?

So I managed to figure out (by looking at what traits the server implements + trying to understand the compilation errors) that the type would be something like that:

fn make_server() -> Box<dyn hyper::rt::Future<Item = (), Error = hyper::Error> + Send> {

  let addr = ([127, 0, 0, 1], 3000).into();

  let make_svc = make_service_fn(|socket: &AddrStream| {
    let remote_addr = socket.remote_addr();
    service_fn_ok(move |_: Request<Body>| {
      Response::new(Body::from(format!("Hello, {}", remote_addr)))
    })
  });

  // Then bind and serve...
  Box::new(Server::bind(&addr).serve(make_svc))
}

Is this correct? Is there a better way?

As for the make_svc I must admit I'm completely lost, I don't really understand what I need to supply as parameter for MakeService (if this is the trait I need to return at all).

Thanks

Try putting it in a separate function and using the wrong return type. I usually use u8. The compiler will fail with an error along the lines Got type Server<..> expected type u8, telling you the return type.

fn make_svc() -> u8 {
  make_service_fn(|socket: &AddrStream| {
    let remote_addr = socket.remote_addr();
    service_fn_ok(move |_: Request<Body>| {
      Response::new(Body::from(format!("Hello, {}", remote_addr)))
    })
  })
}

What error does this give?

1 Like

Thanks @alice, I tried that and it gave the following (which was not very helpful):

mismatched types
expected u8, found struct `hyper::service::make_service::MakeServiceFn`
note: expected type `u8`
         found type `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 

MakeServiceFn is not exported (nor do I know how to convert the specific clojure it returns).

Ah I see what the issue is. Looking at the documentation the MakeServiceFn is not a link, so it is not exported as you said.

It's really unfortunate when library authors don't export their return types, because it makes working with them much more difficult. In this case the important part about the MakeServiceFn struct is that it implements MakeService. Since this is all you need to know about it, you can return it with the impl trait feature.

Annoyingly the MakeService trait have quite a few associated types, so the impl MakeService will be rather long. You could do this and see what it says the real associated types should be.

fn make_svc() -> impl MakeService<ReqBody=u8, ResBody=u8, Error=u8, Service=u8, Future=u8, MakeError=u8> {

If you need impl Trait on any associated type, you must use a Box, since impl Trait cannot be nested.

fn make_svc() -> Box<dyn MakeService<ReqBody=impl Trait, ResBody=u8, Error=u8, Service=u8, Future=u8, MakeError=u8>> {
     Box::new(...

Of course, you could also make your own struct and implement MakeService on it, ignoring this make_service_fn mess.

If we look in the source they do give a reason for why it was not exported:

// Not exported from crate as this will likely be replaced with `impl Service`.
pub struct MakeServiceFn<F> {
    f: F,
}

I really appreciate your patience :clap:, however, it strikes again:

wrong number of type arguments: expected 1, found 0
expected 1 type argumentrustc(E0107)

Trying to follow your example I changed the return type to this:


impl MakeService<u8, ReqBody=u8, ResBody=u8, Error=u8, Service=u8, Future=u8, MakeError=u8> 

and now the error is:

expected a `std::ops::Fn<()>` closure, found `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>`
expected an `Fn<()>` closure, found `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>`
help: the trait `std::ops::Fn<()>` is not implemented for `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>`
note: wrap the `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>` in a closure with no arguments: `|| { /* code */ }
note: required because of the requirements on the impl of `hyper::service::NewService` for `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>`
note: the return type of a function must have a statically known sizerustc(E0277)
my_example.rs(45, 18): expected an `Fn<()>` closure, found `hyper::service::make_service::MakeServiceFn<[closure@examples/my_example.rs:46:19: 51:4]>`

At this point I give up. I'll embed the make_svc inside the function and try to get some sleep :slightly_frowning_face:.

Thanks a lot for your time and effort.

Yeah it's a really annoying situation and I too need to go to bed. If you want to refactor it out of main, I recommend creating a custom struct and implementing the trait on your struct.