I’m building a small HTTP library with both sync and async runtimes (Tokio,async-std or smol).
I'm using Rust edition 2024, and for this crate I' trying to keep dependencies as few and as stable/basic as possible.
I want users to register handlers by passing plain functions—sync or async—without wrappers:
- Sync:
fn(&Request) -> Response
- Async:
async fn(&Request) -> Response
Call site I want to keep:
server.add_route("/", Rt::GET, demo_get); // no wrappers
Minimal example
// main.rs (Tokio)
#[tokio::main]
async fn main() {
let mut server = Server::new("127.0.0.1:7878", None).await.unwrap();
server.add_route("/", Rt::GET, demo_get); // ← keep this as-is
server.run().await;
}
async fn demo_get(_req: &Request) -> Response {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Response { status: "200 OK".into(), content_type: "".into(), content: b"OK".to_vec() }
}
What I tried (many variants, none fully solved it)
A) async-trait
+ Arc<dyn Handler>
: mixed sync/async modes got complicated (lifetimes, for<'a>, cfg).
B) for<'a> + boxed future closure type:
for<'a> Fn(&'a Request) -> Pin<Box<dyn Future<Output = Response> + Send + 'a>>
Still hit “expected impl for<'a> Future
, found impl Future
” (opaque impl Trait
mismatch).
C) Wrapper at call site (works but I want to avoid it):
server.add_route("/", Rt::GET, |r| Box::pin(demo_get(r)));
D) futures::future::BoxFuture<'a, Response>
as the handler return type:
Store Arc<dyn for<'a> Fn(&'a Request) -> BoxFuture<'a, Response> + Send + Sync>
and Box::pin(f(req))
inside add_route
. This keeps the call site clean and compiles.
Current error
error[E0308]: mismatched types
--> src/main.rs:62:3
|
62 | server.add_route("/", Rt::GET, demo_get);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected opaque type `impl for<'a> Future<Output = Response>`
found opaque type `impl Future<Output = Response>`
= note: distinct uses of `impl Trait` result in different opaque types
note: the lifetime requirement is introduced here
--> .../http-server/src/runtime/async/tokio.rs:43:35
|
43 | F: for<'a> Fn(&'a Request) -> Fut + Send + Sync + 'static,
| ^^^
Any tip for solving this would be very appreciated, since I have been looping on AI suggested solutions for a while now.