Can't use yoke with axum extractors
#7024
-
|
axum = { version = "0.8.4", features = ["tokio", "http2"] }
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = { version = "1.0.143" }
bytes = { version = "1.10.1" }
yoke = { version = "0.8.0", features = ["derive", "serde", "alloc"] }
use axum::{Router, extract::FromRequest, routing::get};
use serde::Deserialize;
use yoke::{Yoke, Yokeable};
#[derive(Yokeable, Deserialize)]
pub struct Request<'a> {
data: &'a str,
}
pub struct Extractor<T>(Yoke<T, Vec<u8>>)
where
for<'a> T: Yokeable<'a, Output = T>;
impl<T, S> FromRequest<S> for Extractor<T>
where
S: Send + Sync,
for<'a> T: Yokeable<'a, Output = T>,
T: Deserialize<'static>
{
type Rejection = ();
async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection>
{
Ok(Self(Yoke::attach_to_cart(
::bytes::Bytes::from_request(req, state)
.await
.unwrap()
.to_vec(),
|v| serde_json::from_slice::<T>(v).unwrap(),
)))
}
}
async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}This is what rustc complains about: Spoilererror[E0521]: borrowed data escapes outside of closure
--> mycrate/examples/test.rs:29:17
|
29 | |v| serde_json::from_slice::<T>(v).unwrap(),
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | `v` escapes the closure body here
| | argument requires that `'1` must outlive `'static`
| `v` is a reference that is only valid in the closure body
| has type `&'1 [u8]`
error: incompatible lifetime on type
--> mycrate/examples/test.rs:34:42
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: because this has an unmet lifetime requirement
--> mycrate/examples/test.rs:12:16
|
12 | for<'a> T: Yokeable<'a, Output = T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement
note: the lifetime `'a` as defined here...
--> mycrate/examples/test.rs:34:18
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^
note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
--> mycrate/examples/test.rs:5:10
|
5 | #[derive(Yokeable, Deserialize)]
| ^^^^^^^^
= note: this error originates in the derive macro `Yokeable` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> mycrate/examples/test.rs:34:42
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected struct `Request<'a>`
found struct `Request<'a>`
note: the lifetime `'a` as defined here doesn't meet the lifetime requirements
--> mycrate/examples/test.rs:34:18
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^
note: the lifetime requirement is introduced here
--> mycrate/examples/test.rs:12:29
|
12 | for<'a> T: Yokeable<'a, Output = T>;
| ^^^^^^^^^^
error[E0308]: mismatched types
--> mycrate/examples/test.rs:34:42
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected struct `Request<'a>`
found struct `Request<'a>`
note: the required lifetime does not necessarily outlive the lifetime `'a` as defined here
--> mycrate/examples/test.rs:34:18
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^
note: the lifetime requirement is introduced here
--> mycrate/examples/test.rs:12:29
|
12 | for<'a> T: Yokeable<'a, Output = T>;
| ^^^^^^^^^^
warning: unused variable: `payload`
--> mycrate/examples/test.rs:34:32
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^^^^^^ help: if this is intentional, prefix it with an underscore: `_payload`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> mycrate/examples/test.rs:34:72
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| -- lifetime `'a` defined here ^^ returning this value requires that `'a` must outlive `'static`
error[E0308]: mismatched types
--> mycrate/examples/test.rs:34:72
|
34 | async fn handler<'a>(Extractor(payload): Extractor<Request<'a>>) -> () {}
| ^^ one type is more general than the other
|
= note: expected struct `Request<'a>`
found struct `Request<'_>`
note: the lifetime requirement is introduced here
--> mycrate/examples/test.rs:12:29
|
12 | for<'a> T: Yokeable<'a, Output = T>;
| ^^^^^^^^^^
error[E0277]: the trait bound `for<'a> fn(Extractor<Request<'a>>) -> impl Future<Output = ()> {handler}: Handler<_, _>` is not satisfied
--> mycrate/examples/test.rs:38:44
|
38 | let app = Router::new().route("/", get(handler));
| --- ^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `for<'a> fn(Extractor<Request<'a>>) -> impl Future<Output = ()> {handler}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
= help: the following other types implement trait `Handler<T, S>`:
`Layered<L, H, T, S>` implements `Handler<T, S>`
`MethodRouter<S>` implements `Handler<(), S>`
note: required by a bound in `axum::routing::get`
--> /home/<REDACTED>/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.4/src/routing/method_routing.rs:441:1
|
441 | top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
| | |
| | required by a bound in this function
| required by this bound in `get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
Some errors have detailed explanations: E0277, E0308, E0521.
For more information about an error, try `rustc --explain E0277`.
warning: `mycrate` (example "test") generated 1 warning
error: could not compile `mycrate` (example "test") due to 7 previous errors; 1 warning emitted |
Beta Was this translation helpful? Give feedback.
Answered by
Manishearth
Oct 2, 2025
Replies: 1 comment
-
|
Your problem is When you have a use axum::{Router, extract::FromRequest, routing::get};
use serde::Deserialize;
use yoke::{Yoke, Yokeable};
#[derive(Yokeable, Deserialize)]
pub struct Request<'a> {
data: &'a str,
}
pub struct Extractor<T>(Yoke<T, Vec<u8>>) where for<'a> T: Yokeable<'a>;
impl<T, S> FromRequest<S> for Extractor<T>
where
S: Send + Sync,
for<'a> T: Yokeable<'a>,
for<'a> <T as Yokeable<'a>>::Output: Deserialize<'a>
{
type Rejection = ();
async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection>
{
Ok(Self(Yoke::attach_to_cart(
::bytes::Bytes::from_request(req, state)
.await
.unwrap()
.to_vec(),
|v| serde_json::from_slice::<<T as Yokeable>::Output>(v).unwrap(),
)))
}
}
async fn handler(Extractor(payload): Extractor<Request<'static>>) -> () {}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
} |
Beta Was this translation helpful? Give feedback.
0 replies
Answer selected by
1xX69
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your problem is
T: Yokeable<'a, Output = T>, the T type and the output types are different types (they are the same types with different lifetimes).When you have a
Yoke<T>,Tis always the'staticversion of the type, and theOutputis the borrowed version.