How to solve this serde deserialize lifetime issue

I've two structs described below

use serde::{Serialize, Deserialize};
use std::collections::HashMap; // 1.0.130
#[derive(Serialize, Deserialize)]
pub struct StarWarsChar {
    id: &'static str,
    name: &'static str,
    is_human: bool,
    home_planet: Option<&'static str>,
    primary_function: Option<&'static str>,
}

#[derive(Serialize, Deserialize)]
pub struct StarWars<'a> {
    #[serde(borrow)]
    char_by_id: HashMap<&'a str, usize>,
    chars: Vec<StarWarsChar>,
    relations: Vec<Vec<usize>>,
}

(Playground)

This are the errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined here...
  --> src/lib.rs:12:21
   |
12 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   = note: expected `SeqAccess<'_>`
              found `SeqAccess<'de>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   = note: expected `Deserialize<'_>`
              found `Deserialize<'static>`
   = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined here...
  --> src/lib.rs:12:21
   |
12 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   = note: expected `MapAccess<'_>`
              found `MapAccess<'de>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
  --> src/lib.rs:16:5
   |
16 |     chars: Vec<StarWarsChar>,
   |     ^^^^^
   = note: expected `Deserialize<'_>`
              found `Deserialize<'static>`
   = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground` due to 2 previous errors

How to solve this?

Atleast please tell me is it possible tell me is it possible or not?

I suspect you would be better off using String here.

1 Like

if StarWarsChar has the same lifetime has StarWars it works. With 'static StarWarsChar can outlive StarWars which 'a disallow. With this solution you can choose between 'static or others.

use serde::{Serialize, Deserialize};
use std::collections::HashMap; // 1.0.130
#[derive(Serialize, Deserialize)]
pub struct StarWarsChar<'b> {
    id: &'b str,
    name: &'b str,
    is_human: bool,
    home_planet: Option<&'b str>,
    primary_function: Option<&'b str>,
}

#[derive(Serialize, Deserialize)]
pub struct StarWars<'a> {
    #[serde(borrow)]
    char_by_id: HashMap<&'a str, usize>,
    chars: Vec<StarWarsChar<'a>>,
    relations: Vec<Vec<usize>>,
}

I found a better solution by indicating that 'a lifetime doesn't exceed 'b one:

use serde::{Serialize, Deserialize};
use std::collections::HashMap; // 1.0.130
#[derive(Serialize, Deserialize)]
pub struct StarWarsChar<'b> {
    id: &'b str,
    name: &'b str,
    is_human: bool,
    home_planet: Option<&'b str>,
    primary_function: Option<&'b str>,
}

#[derive(Serialize, Deserialize)]
pub struct StarWars<'a: 'b, 'b> {
    #[serde(borrow)]
    char_by_id: HashMap<&'a str, usize>,
    chars: Vec<StarWarsChar<'b>>,
    relations: Vec<Vec<usize>>,
}

You can use static for StarWarsChar and other for StarWars. Disadvantage you have 2 generics

Actually, it's only partial part of the problem. Your code compiles until I try to do this:

fn main() {
    let t: StarWars = bson::from_reader(std::fs::File::open("starwars.bson").unwrap()).unwrap();

}

Then it gives these error:
(error for 1st answer)

error: implementation of `Deserialize` is not general enough
   --> src/main.rs:232:23
    |
200 |     let t: StarWars = bson::from_reader(std::fs::File::open("starwars.bson").unwrap()).unwrap();
    |                       ^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
    |
    = note: `StarWars<'_>` must implement `Deserialize<'0>`, for any lifetime `'0`...
    = note: ...but `StarWars<'_>` actually implements `Deserialize<'1>`, for some specific lifetime `'1`

(error for 2nd example)

error: implementation of `Deserialize` is not general enough
   --> src/main.rs:233:23
    |
200 |     let t: StarWars = bson::from_reader(std::fs::File::open("starwars.bson").unwrap()).unwrap();
    |                       ^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
    |
    = note: `StarWars<'_, '_>` must implement `Deserialize<'0>`, for any lifetime `'0`...
    = note: ...but `StarWars<'_, '_>` actually implements `Deserialize<'1>`, for some specific lifetime `'1`

Interesting enough, it will write the data into file if you hard code the data and send it to bson for writing to a file. However reading from the file is an issue

Well, it's perfectly logical if you think about it. When you serialize the data, then it doesn't matter what the lifetime is: it will get copied/generated out of the struct and then it will be sent over to a file (for example) where it is stored. However, when you are deserializing, it will be your type that is supposed to store the deserialized data. So it will have to have an owner. This means that unless you are deserializing from an already-owning buffer, for instance, you can't in general deserialize into references. So you really should use an owned type (e.g. String) if you want to support deserializing from arbitrary sources.

2 Likes

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.