How to implement serde::Deserialize on struct containing &'static str?

Viewed 1344

I'm trying to implement serde::Deserialize on a SourceConfig struct, which wraps a struct containing an &'static str along with having some data of its own (playground):

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct Config {
    pub name: &'static str,
}

#[derive(Serialize, Deserialize)]
struct SourceConfig {
    config: Config,
    id: u32,
}

But that gives me a lifetime error:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'de` due to conflicting requirements
  --> src/lib.rs:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined on the impl at 8:21...
  --> src/lib.rs:8:21
   |
8  | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   = 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:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   = note: expected `Deserialize<'_>`
              found `Deserialize<'static>`
   = note: this error originates in a derive macro (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:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined on the impl at 8:21...
  --> src/lib.rs:8:21
   |
8  | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   = 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:10:5
   |
10 |     config: Config,
   |     ^^^^^^
   = note: expected `Deserialize<'_>`
              found `Deserialize<'static>`
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 2 previous errors

I tried adding #[serde(borrow)] to the config in SourceConfig, but that doesn't work since Config isn't borrowed. Adding it to the name in Config doesn't work either. How can I correctly implement Deserialize on SourceConfig?

1 Answers

You can add a bound on the SourceConfig struct, such that the 'de lifetime is equal to the 'static lifetime.

#[derive(Debug, serde::Deserialize)]
pub struct Config {
    pub name: &'static str,
}

#[derive(Debug, serde::Deserialize)]
#[serde(bound(deserialize = "'de: 'static"))]
struct SourceConfig {
    config: Config,
    id: u32,
}

fn main() {
    let j = r#"
{
    "id": 123,
    "config": {
        "name": "John Smith"
    }
}
    "#;
    
    let sc: SourceConfig = serde_json::from_str(&j).unwrap();
    dbg!(sc);
}

However, you probably do not want to deserialize a &'static str since that requires that you have a &'static str to borrow from. This limits you to either string literals, or you need to leak memory to acquire a &'static str. You likely want a String or, if you want to support string literals, a Cow<'a, str>.

Another problem with &'a str is that it will not work if the string is serialized with escape sequences. For example a JSON string containing a line-break will have \n which need to be replaced during deserialization thus failing if you use &'a str.

Related