Search code examples
rustjson-deserializationlifetimeserde

Lifetime issue while implementing a trait for a generic type which implements a trait with an associated lifetime


I am writing a deserializer (Vec<u8> (Raw JSON) to any type T) for a project and trying to use serde for it. Here's my trait which I to decode the data:

pub trait Decode: Sized {
    fn try_decode(input: Vec<u8>) -> Result<Self, InternalError>;
}

Since I intend to use #[derive(Deserialize)] on my structs, I am writing an impl for any type that implements the Deserialize trait with an arbitrary lifetime:

impl<'a, T: Deserialize<'a>> Decode for T {
    fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
        if let Ok(t) = serde_json::from_slice(&input) {
            Ok(t)
        } else {
            Err(Internal::decode("Could not decode"))
        }
    }
}

But I am getting a lifetime error:

error[E0597]: `input` does not live long enough
  --> src/decode.rs:16:47
   |
14 | impl<'a, T: Deserialize<'a>> Decode for T {
   |      -- lifetime `'a` defined here
15 |     fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
16 |         if let Ok(t) = serde_json::from_slice(&input) {
   |                        -----------------------^^^^^^-
   |                        |                      |
   |                        |                      borrowed value does not live long enough
   |                        argument requires that `input` is borrowed for `'a`
...
21 |     }
   |     - `input` dropped here while still borrowed

I understand why the compiler is mad at me. Upon checking the implementation of from_slice, I figured it has an associated lifetime. I am sure input is an owned value, but from_slice requires a reference, so here I am returning reference of a value which gets dropped when the function ends, which is clearly incorrect. My question is, is there a way to ensure that the value is borrowed for <'a> lifetime (i.e. the lifetime supplied by the caller of this function). I can't update the trait because it will cause a disaster.


Solution

  • If you want a value that doesn't borrow from the slice, you can use DeserializeOwned as the trait bound, which has no associated lifetime (and therefore only stores owned values). For example, this compiles:

    impl<T: DeserializeOwned> Decode for T {
        fn try_decode(input: Vec<u8>) -> Result<Self, InternalError> {
            if let Ok(t) = serde_json::from_slice(&input) {
                Ok(t)
            } else {
                todo!()
            }
        }
    }
    

    Playground

    This is equivalent to using a higher-ranked trait bound with the lower-level Deserialize trait bound:

    impl<T: for<'de> Deserialize<'de>> Decode for T {
        // ...
    }