axum
axum copied to clipboard
`#[debug_handler]` doesn't improve error message for `WithRejection`
WithRejection<T, E> requires E to implement IntoResponse. If you forget that you get the familiar "handler not implemented" error and #[debug_handler] doesn't help:
use axum::extract::rejection::JsonRejection;
use axum::response::{IntoResponse, Response};
use axum::routing::{post, MethodRouter};
use axum::Json;
use axum_extra::extract::WithRejection;
#[tokio::main]
async fn main() {
let _: MethodRouter = post(send);
}
#[axum_macros::debug_handler]
pub async fn send(
_: WithRejection<Json<MessageSend>, ApiError>,
) -> Json<MessageSendResponse> {
Json(MessageSendResponse { id: "".to_string() })
}
#[derive(serde::Deserialize)]
pub struct MessageSend {
pub contents: String,
}
#[derive(serde::Serialize)]
pub struct MessageSendResponse {
pub id: String,
}
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error(transparent)]
JsonExtractorRejection(#[from] JsonRejection),
}
// no `impl IntoResponse for ApiError`
The error message is
error[E0277]: the trait bound `fn(WithRejection<Json<MessageSend>, ApiError>) -> impl Future<Output = Json<MessageSendResponse>> {send}: Handler<_, _, _>` is not satisfied
--> src/main.rs:9:32
|
9 | let _: MethodRouter = post(send);
| ---- ^^^^ the trait `Handler<_, _, _>` is not implemented for `fn(WithRejection<Json<MessageSend>, ApiError>) -> impl Future<Output = Json<MessageSendResponse>> {send}`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `Handler<T, S, B>`:
<IntoHandler<H, T, S, B> as Handler<T, S, B>>
<Layered<L, H, T, S, B> as Handler<T, S, B>>
<Or<L, R, Lt, Rt, S, B> as Handler<(M, Lt, Rt), S, B>>
note: required by a bound in `post`
--> /Users/davidpdrsn/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.6.0-rc.1/src/routing/method_routing.rs:404:1
|
404 | top_level_handler_fn!(post, POST);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `post`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `WithRejection<Json<MessageSend>, ApiError>: FromRequest<(), Body, _>` is not satisfied
--> src/main.rs:14:8
|
14 | _: WithRejection<Json<MessageSend>, ApiError>,
| ^^^^^^^^^^^^^ the trait `FromRequest<(), Body, _>` is not implemented for `WithRejection<Json<MessageSend>, ApiError>`
|
= help: the trait `FromRequest<S, B>` is implemented for `WithRejection<E, R>`
note: required by a bound in `__axum_macros_check_send_0_from_request_check`
The generate code is
#[allow(warnings)]
fn __axum_macros_check_send_0_from_request_check<M>()
where
WithRejection<Json<MessageSend>, ApiError>:
::axum::extract::FromRequest<(), axum::body::Body, M> + Send,
{
}
#[allow(warnings)]
fn __axum_macros_check_send_0_from_request_call_check() {
__axum_macros_check_send_0_from_request_check();
}
For some reason the additional M type parameter changes rust's error message. If you remove that and instead generate
#[allow(warnings)]
fn __axum_macros_check_send_0_from_request_check()
where
WithRejection<Json<MessageSend>, ApiError>:
::axum::extract::FromRequest<(), axum::body::Body> + Send,
{
}
The error message becomes
error[E0277]: the trait bound `ApiError: IntoResponse` is not satisfied
--> src/main.rs:36:5
|
36 | / WithRejection<Json<MessageSend>, ApiError>:
37 | | ::axum::extract::FromRequest<(), axum::body::Body> + Send,
| |_________________________________________________________________^ the trait `IntoResponse` is not implemented for `ApiError`
|
We could look into this and see if its possible to improve this.
For some reason the additional
Mtype parameter changes rust's error message. If you remove that and instead generate
I think this is because WithRejection implements FromRequest for two different values of M, once directly, and once through FromRequests blanket impl. If rustc knows which one of the two is wanted, it elaborates on why it doesn't apply. If rustc doesn't know, it doesn't elaborate, presumably because it could lead to an explosion of further elaborations, rather than just a chain.
I thinking we could just build in special support for WithRejection, similarly to what we for Path. If we cannot find a good general solution.
Right, so I think what we can do is to "split out" the assertions for WithRejection (and other generic extractors that wrap another extractor). I.e. instead of the current
WithRejection<Json<MessageSend>, ApiError>: FromRequest<(), axum::body::Body> + Send,
we can generate
Json<MessageSend>: ::axum::extract::FromRequest<(), axum::body::Body> + Send,
ApiError:
From<<Json<MessageSend> as FromRequest<(), axum::body::Body>::Rejection> + IntoResponse,