Search code examples
rusthyper

Deserializing hyper body into utf8 string: error[E0597]: `bytes` does not live long enough


I want to write a util where I deserialize a given request body into the target type (T):

pub async fn read_json_body<'a, T>(req: &'a mut Request<Body>) -> T where T : Deserialize<'a> {
    let mut body = req.body_mut();
    let bytes = body::to_bytes(body).await.unwrap();
    let body_str = std::str::from_utf8(&*bytes).unwrap();
    serde_json::from_str::<T>(body_str).unwrap()
}

When I try to compile the code, I get the following error:

error[E0597]: `bytes` does not live long enough

21 |     pub async fn read_json_body<'a, T>(req: &'a mut Request<Body>) -> T where T : Deserialize<'a> {
   |                                 -- lifetime `'a` defined here
...
24 |         let body_str = std::str::from_utf8(&*bytes).unwrap();
   |                                              ^^^^^ borrowed value does not live long enough
25 |         serde_json::from_str::<T>(body_str).unwrap()
   |         ----------------------------------- argument requires that `bytes` is borrowed for `'a`
26 |     }
   |     - `bytes` dropped here while still borrowed

I understand that the Deserialize trait requires the lifetime 'a to outlive the deserialization, which is why assign the lifetime to the request reference. However I don't understand what my options are to fix this error in regard to the lifetime of the bytes object.

How do I fix this issue?

Requirements:

  • I don't want to move the request into the scope of the function
  • I don't want to extract the body from the request outside of the util, the request should be the parameter for the function, not the body

Solution

  • Use an owned version constraining to DeserializeOwned instead:

    pub async fn read_json_body<T>(req: &mut Request<Body>) -> T where T : DeserializeOwned {
        let mut body = req.body_mut();
        let bytes = body::to_bytes(body).await.unwrap();
        let body_str = std::String::from_utf8(&*bytes).unwrap();
        serde_json::from_str::<T>(&body_str).unwrap()
    }