I am trying to build a Tokio+Hyper Web server in rust and I am struggling to juggle the requirements of those async/multithread systems.
here are the dependencies of the program:
[dependencies]
hyper = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"] }
http-body-util = "0.1"
hyper-util = { version = "0.1", features = ["full"] }
and here is the program:
use std::convert::Infallible;
use std::net::SocketAddr;
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use std::future::Future;
#[derive(Debug,Clone)]
struct TestStruct {
a : i32,
}
async fn query_dispatcher_test(_req: Request<hyper::body::Incoming>, _pool : &TestStruct) -> Result<Response<Full<Bytes>>, Infallible>
{
Ok(Response::new(Full::new(Bytes::from("default API"))))
}
async fn build_service<F, S>(pool : &TestStruct, listener : &TcpListener, f: F)
where
F: 'static + Fn(Request<hyper::body::Incoming>, &TestStruct) -> S,
F: Sync + Send + Copy,
S: Future<Output = Result<Response<Full<Bytes>>, Infallible>> + Send
{
let (stream, _) = listener.accept().await.unwrap();
// Use an adapter to access something implementing `tokio::io` traits as if they implement
// `hyper::rt` IO traits.
let io: TokioIo<_> = TokioIo::new(stream);
let pool_clone = pool.clone();
let service = service_fn(move |_req : Request<hyper::body::Incoming>| {
let inner = pool_clone.clone();
async move {
f(_req, &inner).await
}
});
// Spawn a tokio task to serve multiple connections concurrently
tokio::task::spawn(async move {
// Finally, we bind the incoming connection to our service
if let Err(err) = http1::Builder::new()
.serve_connection(io, service)
.await
{
eprintln!("Error serving connection: {:?}", err);
}
});
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;
let pool = TestStruct{
a : 1,
};
// We start a loop to continuously accept incoming connections
loop {
build_service(&pool, &listener,query_dispatcher_test);
}
}
Here is the error:
error[E0308]: mismatched types
--> src/main.rs:64:9
|
64 | build_service(&pool, &listener,query_dispatcher_test);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected opaque type `impl for<'a> Future<Output = Result<Response<http_body_util::Full<hyper::body::Bytes>>, Infallible>>`
found opaque type `impl Future<Output = Result<Response<http_body_util::Full<hyper::body::Bytes>>, Infallible>>`
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
note: the lifetime requirement is introduced here
--> src/main.rs:25:69
|
25 | F: 'static + Fn(Request<hyper::body::Incoming>, &TestStruct) -> S,
| ^
Here is where I am struggling: I know that the problem is the pool
reference in the f
function. If I remove it the program compiles.
Another weird thing is that if I move the content of build_service
inside the loop {}
in main, and replace f
with query_dispatcher_test
and remove my call to build_service
the program compiles fine.
So I know that the problem is related to the lifetime of my pool in the f
function. but because it is moved and cloned, it will live as long as closure provided to service_fn
. I don’t understand what lifetime information the compiler needs.
When it comes to the lifetime information I provide to the function it is related to what Tokyo requires for tokio::task::spawn