Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store a hyper::server::Server as a field in a struct?

Tags:

rust

hyper

I have a library which uses hyper internally. I want the user to be able to create an Appwhich contains a Server internally that handles HTTP connections.

use hyper::server::conn::AddrIncoming;
use hyper::server::Server;
use hyper::service::service_fn_ok;
use std::net::SocketAddr;

pub struct App {
    inner: Server<AddrIncoming, ()>,
}

impl App {
    pub fn new() -> Self {
        let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
        let inner = Server::bind(&addr).serve(|| service_fn_ok(|_req| unimplemented!()));

        App { inner }
    }
}

(Playground link)

The error is, as expected:

error[E0308]: mismatched types
  --> src/lib.rs:15:15
   |
15 |         App { inner }
   |               ^^^^^ expected (), found closure
   |
   = note: expected type `hyper::server::Server<_, ()>`
              found type `hyper::server::Server<_, [closure@src/lib.rs:13:47: 13:88]>`

It's not well documented, but the second type parameter for Server is the kind of MakeService it uses.

I can't figure out how to refer to the closure in the type of inner. Is there some way that I can box the closure to make the code compile? Is there a way to implement MakeService by hand, instead of using a closure?

The hyper docs refer to the function make_service_fn, which returns a MakeServiceFn, but the type isn't public, so I can't use it in the type of inner.

like image 864
muskox Avatar asked Jun 06 '19 00:06

muskox


1 Answers

The problem is due to a type mismatch. In Rust, a type parameter is part of the type of a struct, so the type parameters for the server in your struct must match the ones you defined in your struct. In your case they don't.

There are 2 solutions to your problem.

Add a type parameter for the second server parameter to your struct

pub struct App<T> {
    inner: Server<AddrIncoming, T>,
}

Now you'll be able to create apps with different types for the second type parameter of the server

Find the type of the second argument of the server that you are creating

In your case, the type of the second argument is ``, so you would declare your struct like this:

type Service = ?; // This is really hard to find in this case.
pub struct App {
    inner: Server<AddrIncoming, Service>,
}

Conclusion

In your case, I would recommend the first one because the type of the second type parameter of Server is hard to find and could very well change during the development of your program, so it's much easier to just have a type parameter on your struct.

However, sometimes you won't be able to use certain method on your server if you don't know that its type parameters don't implement certain traits, so you can add these traits to your type parameter like this:

pub struct App<T: Service> {
    inner: Server<AddrIncoming, T>,
}

It is recommended not to put the type parameters on the struct itself and to only put them on the impl blocks:

pub struct App<T> {
    inner: Server<AddrIncoming, T>,
}

impl App<T: Service> {
   // Here you'll be able to use the method from Server where T has to be a Service.
}

You can also do the same for functions like this:

pub struct App<T> {
    inner: Server<AddrIncoming, T>,
}

fn some_function(app: App<T: Service>) {
   // Here you'll be able to use the method from Server where T has to be a Service
}
like image 178
ZakCodes Avatar answered Oct 04 '22 16:10

ZakCodes