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

Hi team, 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 interface 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.

I started using DeserializeOwned instead of Deserialize to get rid of lifetime issues. Thanks guys! :slight_smile:

1 Like

Why couldn't you use serde_json::from_reader or from_slice directly and map_err ? &[u8] implements Read so, in effect it's already implemented for Vec<u8>.

Thanks for your reply. from_slice has an associated lifetime, which says whatever the value we generate, it will have an associate lifetime same as the lifetime of the invoking scope. The value input is owned by the function, but when we do serde_json::from_slice(&input) the lifetime gets passed as the lifetime of the function itself. When the function returns, the input vec is dropped, thus freeing its internal value, invalidating the reference sent out.

I wanted to change the lifetime of the reference returned from serde_json::from_slice to <'a> - which is the lifetime of its caller scope. I do not have info if that can be done at all or now, I will check that and post here if I find something.

Borrowing the argument will work. Borrowing the slice for 'a is the goal, and as far as I can tell that can only by done by the function signature, not within its body.

pub trait Decode<'a>: Sized {
    fn try_decode(input: &'a [u8]) -> Result<Self, String>;
}

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

This playground is what I meant above. I don't see any issues with lifetimes unless your struct uses some zero copy ref.

Yep, it would have worked, but I cannot modify the try_decode trait.

I apologize, I am not completely following. This is my code, can you please explain your method here? I am keen to see how can I make it work with Deserialize<'a> here. I understand what you are suggesting, but it doesn't look applicable in my use case.

Recommended reading:

3 Likes

This is exactly and absolutely what I was looking for, plus it is the first time I am reading about Higher-Ranked Trait Bounds as well!

Had you not posted this, I was to start looking into source code of Serde where implements the Deserialize<'de, T> for types. Now that I have read this blog, I will be much more informed and confident to read the library code. Thanks a lot! :smiley: :star_struck:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.