While playing around with Axum, I ran into the above-mentioned compile-time error. The underlying issue, in my case, was holding a type that’s not Send
across an await
. Read on if you’re interested in the details.
The full error message I was given was the following:
.fallback(fallback_handler);
| -------- ^^^^^^^^^^^^^^^^ the trait `Handler<_, ()>` is not implemented for fn item `fn(Extension<State>, Request<Body>) -> impl Future<Output = impl IntoResponse> {fallback_handler}`
| |
| required by a bound introduced by this call
|
= help: the trait `Handler<T, S, B2>` is implemented for `Layered<L, H, T, S, B, B2>`
The error message didn’t help me in figuring out what’s going on. The most confusing part was that the problem seemed to be caused by instantiating a struct I had just written, but I hadn’t changed anything about the function signature or the return value.
My handler looked something like this:
async fn fallback_handler(
Extension(state): Extension<State>,
request: Request<Body>,
) -> impl IntoResponse {
let mut errors: Vec<Box<dyn Error>> = vec![];
// [...]
let tera = state.tera.lock().await;
// [...]
StatusCode::OK
}
Now, if you’re familiar with async
Rust, you might already see the problem. It’s the same issue described in detail here, namely that
std::error::Error
is notSend
, i.e. it cannot safely be sent between threads- Holding a non-Send type across an
await
results in a non-Send Future (explanation)
Axum’s Handler
type, however, requires the resulting Future to be Send
, as can be seen here:
pub trait Handler<T, S, B = Body>: Clone + Send + Sized + 'static {
/// The type of future calling this handler returns.
type Future: Future<Output = Response> + Send + 'static;
// [...]
}
So, long story short: don’t try to hold non-Send types across await
calls when you’re writing an Axum handler.