0

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.

metamemelord
  • 500
  • 1
  • 7
  • 19
  • 1
    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](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f54236f04db830586ffb62c048d06d66). – user4815162342 May 16 '21 at 19:44
  • Yep, found this solution in other channel, thanks! Answering my own question. – metamemelord May 16 '21 at 19:48

2 Answers2

2

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 {
    // ...
}
user4815162342
  • 141,790
  • 18
  • 296
  • 355
1

The correct way to use Deserialize with a lifetime is using Higher Rank Trait Bounds: Specifically telling compiler that this lifetime is valid for all (concerned) lifetimes using the for<'a> notation. The code will look like this:

impl<T: for<'de> Deserialize<'de>> InputCoercible for T {
    // ...trait methods...
}
metamemelord
  • 500
  • 1
  • 7
  • 19
  • 2
    If his answer helped you, please [mark it as accepted](https://stackoverflow.com/help/someone-answers) instead of posting a separate "thank you" answer so people know that the question has been answered. – Aplet123 May 17 '21 at 01:39
  • 1
    Note that the HRTB shown here and `DeserializeOwned` are equally "correct" and are in fact equivalent ([docs](https://serde.rs/lifetimes.html#trait-bounds)). `DeserializeOwned` was introduced precisely so you wouldn't have to spell out the higher-ranked trait bound every time you need to specify `Deserialize` for any lifetime. – user4815162342 May 25 '21 at 15:33
  • Ah okay! Thank you so much! While you explained exactly how they are functionally similar, I had asked about using Deserialize and how can I validate the life of borrow. Your answer did help me, but I believe knowledge of HRTB was the key. I would really appreciate if you add both HRTB and `DeserializeOwned` to your answer so that I can mark it accepted again. :) – metamemelord May 27 '21 at 23:09
  • Hi, I haven't seen your comment (until now) because it didn't contain an `@...` marker to trigger a notification. I've now edited the answer as requested. – user4815162342 Aug 04 '21 at 06:28