13

How can I derive Deserialize for a struct with objects with different or equal lifetimes inside?

playground

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
struct B<'a> {
    b: &'a str,
}

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
struct C<'a> {
    c: &'a str,
}

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
struct A<'a> {
    b: B<'a>,
    c: C<'a>,
}

fn main() {
}

Rustc says this is impossible:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
  --> src/main.rs:13:5
   |
13 |     b: B<'a>,
   |     ^
   |
note: first, the lifetime cannot outlive the lifetime 'de as defined on the impl at 11:26...
  --> src/main.rs:11:26
   |
11 | #[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
   |                          ^^^^^^^^^^^^^^^^^^
   = note: ...so that the types are compatible:
           expected _IMPL_SERIALIZE_FOR_B::_serde::de::SeqAccess<'_>
              found _IMPL_SERIALIZE_FOR_B::_serde::de::SeqAccess<'de>
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 12:10...
  --> src/main.rs:12:10
   |
12 | struct A<'a> {
   |          ^^
   = note: ...so that the types are compatible:
           expected _IMPL_SERIALIZE_FOR_B::_serde::Deserialize<'_>
              found _IMPL_SERIALIZE_FOR_B::_serde::Deserialize<'_>

I don't understand what causes this problem and how I can fix it. There is a similar question but its answer does not cover this case.

trent
  • 25,033
  • 7
  • 51
  • 90
VP.
  • 15,509
  • 17
  • 91
  • 161
  • I think it's because of the `serde::Deserialize` and `serde::Serialize`, deleting those fix the problem. No idea how to keep those Traits without errors – Davichete Aug 28 '19 at 12:52
  • Why don't you use 2 different lifetimes? – Boiethios Aug 28 '19 at 12:55
  • Of course it's related to `serde::Deserialize`, that's why I added the "serde" tag to the question. But deleting them isn't really a fix. – Denys Séguret Aug 28 '19 at 12:55
  • 1
    Simpler reproduction: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0a3bb11afd9b8c85e5c8b2228b1420fa – Boiethios Aug 28 '19 at 12:58
  • Different lifetimes do not work as well, of course I have already tried :) – VP. Aug 28 '19 at 12:59
  • @VictorPolevoy I know, that's unrelated to your question, but that doesn't make a lot of sense to have a single lifetime for 2 unrelated data. – Boiethios Aug 28 '19 at 13:00
  • 3
    This seems to work : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cbff07ed91a01bd07cf66dc706edd83d – Denys Séguret Aug 28 '19 at 13:05
  • @VictorPolevoy Consider using String instead of &str, avoid using lifetimes specifiers and solves the error – Davichete Aug 28 '19 at 13:06
  • 1
    @Davichete the main question here is using exactly `&str` instead of `String` :) objects – VP. Aug 28 '19 at 13:22
  • @DenysSéguret yes, it works with `serde borrow` indeed! :) – VP. Aug 28 '19 at 13:23
  • 2
    @VictorPolevoy just be aware that it won't work if the string contains escaped characters -> deserializer must unescape them -> it must be owned (`String`). [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=33fbb7a766642e30c0b577d9164c36fa). – zrzka Aug 28 '19 at 13:35

1 Answers1

21

serde's lifetimes are complex enough to allow you to deserialize without copying data more than necessary. It's described in https://serde.rs/lifetimes.html

Apart for &str and &[u8], serde doesn't accept implicit borrowing.

For other struct parameters, if you want to borrow from the deserializer, you have to be explicit, which is done using a special #[serde(borrow)] attribute:

#[derive(Default, Debug, serde::Deserialize, serde::Serialize)]
struct A<'a> {

    #[serde(borrow)]
    b: B<'a>,

    #[serde(borrow)]
    c: C<'a>,
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758