Generic handler function does not implement Handler trait #3589
-
SummaryHi, I would like to define an api separately from the implementation and therefore try to use generic traits in handler functions. Unfortunately I am not good enough with rust to decipher the compiler error, yet. In the following minimal example, the "specific" implementation works, but the "generic" one does not with the attached error, despite looking almost identical. Any help is much appreciated! use std::convert::Infallible;
use axum::{extract::FromRequestParts, http::StatusCode};
#[derive(Clone)]
struct State;
trait HandlerTrait<S>
where
S: Clone + Send + Sync + 'static,
Self: FromRequestParts<S>,
{
async fn foo(&self) -> StatusCode;
}
struct HandlerImpl<S>(S);
impl<S> HandlerTrait<S> for HandlerImpl<S>
where
S: Clone + Send + Sync + 'static,
{
async fn foo(&self) -> StatusCode {
StatusCode::OK
}
}
impl<S> FromRequestParts<S> for HandlerImpl<S>
where
S: Clone + Send + Sync + 'static,
{
type Rejection = Infallible;
async fn from_request_parts(
_parts: &mut axum::http::request::Parts,
state: &S,
) -> Result<Self, Self::Rejection> {
Ok(HandlerImpl(state.to_owned()))
}
}
mod specific {
use super::*;
use axum::{Router, response::IntoResponse, routing::get};
async fn foo<S>(handler: HandlerImpl<S>) -> impl IntoResponse
where
S: Clone + Send + Sync + 'static,
{
handler.foo().await
}
pub fn routes<S>() -> Router<S>
where
S: Clone + Send + Sync + 'static,
{
Router::new().route("/foo", get(foo::<S>))
}
}
mod generic {
use super::*;
use axum::{Router, response::IntoResponse, routing::get};
async fn foo<S, H>(handler: H) -> impl IntoResponse
where
S: Clone + Send + Sync + 'static,
H: HandlerTrait<S> + 'static,
{
handler.foo().await
}
pub fn routes<S, H>() -> Router<S>
where
S: Clone + Send + Sync + 'static,
H: HandlerTrait<S> + 'static,
{
Router::new().route("/foo", get(foo::<S, H>))
// ^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for
// fn item `fn(H) -> impl Future<Output = impl IntoResponse> {generic::foo::<S, H>}`
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_routes_specific() {
let _router = specific::routes::<State>();
}
#[test]
fn test_routes_generic() {
let _router = generic::routes::<State, HandlerImpl<State>>();
}
}axum version0.8.7 |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
|
The question might be linked to #3582 (?) |
Beta Was this translation helpful? Give feedback.
-
|
If you comment out the problematic In general, this sort of thing can be very hard. I would recommend you try to not make your router-building functions generic, and instead use dynamic dispatch for any functionality you want to conditionally swap out (e.g. your appstate might contain a |
Beta Was this translation helpful? Give feedback.
If you comment out the problematic
routesfunction and anything else that produces hard errors, you should get a warning from the compiler aboutasync fnin traits that tells you to refactor to-> impl Future<…> + Send. Try that.In general, this sort of thing can be very hard. I would recommend you try to not make your router-building functions generic, and instead use dynamic dispatch for any functionality you want to conditionally swap out (e.g. your appstate might contain a
Box<dyn DatabaseAccess>if you want to instantiate your router with a real DB layer for running, and with a mocked one for testing).