Lifetime error with serde_json_borrow::Value in a struct

Hi, I would like to have a struct with one of its members using borrowed data from a JSON document. The member in question attributes can hold any valid JSON (other members are defined by a schema). I looked around and I found this topic on zero-copy json with serde, which led me to the serde_json_borrow crate. serde_json_borrow has a borrowed Value type, which does what I need.

Simple deserialization directly into a borrowed value works well:

let m_value: serde_json_borrow::Value = serde_json::from_str(json_str).unwrap();

However, when I include the borrowed Value in a struct, I'm not able to deserialize, because it seems that the data with lifetime 'a does not outlive the lifetime of the deserializer 'de.

I think I have to mark the input data with lifetime 'a somehow, and my first attempt was this (which didn't work):

pub fn deserialize_with_borrow<'a>(json_str: &'a str) -> Model<'a> {
    let m: Model = serde_json::from_str(json_str).unwrap();
    m
}

Then I thought I somehow need to tell the deserializer to return the data with lifetime 'a, and that is where I am at currently.

Below is a reproducible example. Sorry, no playground link because there no serde_json_borrow there.

use serde::de::Visitor;
use serde::{Deserialize, Serialize};
use serde_json_borrow::Value;
use serde_json::from_str;

#[derive(Debug, Deserialize, Serialize)]
pub struct Model<'a> {
    #[serde(deserialize_with = "deserialize_attributes")]
    pub attributes: Option<Attributes<'a>>,
}

type Attributes<'a> = serde_json_borrow::Value<'a>;

pub fn deserialize_attributes<'de, 'a: 'de, D>(
    deserializer: D,
) -> std::result::Result<Option<Attributes<'a>>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    let s = serde_json_borrow::Value::deserialize(deserializer)?;
    Ok(Some(s))
}

#[test]
fn test() {
    let json_str = r#"{ "attributes": {
        "string": "value1",
        "int": 42,
        "object": { "float": 42.1 },
        "array": ["item1", "item2"]
    } }"#;
    // This works
    let m_value: serde_json_borrow::Value = serde_json::from_str(json_str).unwrap();
    // Lifetime error, `'a` must outlive `'de`
    let m: Model = serde_json::from_str(json_str).unwrap();
    println!("{:?}", m);
}

I'm getting this error:

   |
14 | pub fn deserialize_attributes<'de, 'a: 'de, D>(
   |                               ---  -- lifetime `'a` defined here
   |                               |
   |                               lifetime `'de` defined here
...
21 |     Ok(Some(s))
   |     ^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'de`
   |

Does this mean that I need to manually implement Deserialize and somehow include the lifetime 'a in the return type? Or am I missing something else?

Have you tried using #[serde(borrow)]? Deserializer lifetimes · Serde

Yes I did try that too, but also got an ‘a does not outlive ‘de error.

Firstly, you need #[serde(borrow)], but only that will not solve the error.

The lifetime of a value returned by the deserialize is 'de. But in fn deserialize_attributes<'de, 'a: 'de, D> you are saying 'a: 'de, that means 'a is at least as long as 'de, i.e. 'a is longer than 'de or equal. The value you are borrowing from deserialize may outlive the data you are borrowing from.

The lifetime should be 'de: 'a - 'de is at least as long as 'a, ensuring Model will not outlive the json_str.

1 Like

True, 'de: 'a does solve the issue, thank you!

Yeah, that's exactly what I wanted actually, but I was wrong. I equated 'a with the lifetime of the json string that goes into the deserializer. So I thought that by putting 'a on the Model, the Model will live as long as the json string.
I'll study the lifetimes some more :slight_smile:

The updated, working code:

#[derive(Debug, Deserialize, Serialize)]
pub struct Model<'a> {
    #[serde(borrow, deserialize_with = "deserialize_attributes")]
    pub attributes: Option<Attributes<'a>>,
}

type Attributes<'a> = serde_json_borrow::Value<'a>;

pub fn deserialize_attributes<'de: 'a, 'a, D>(
    deserializer: D,
) -> std::result::Result<Option<Attributes<'a>>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    let s = serde_json_borrow::Value::deserialize(deserializer)?;
    Ok((!s.is_null()).then_some(s))
}

If the Model is to borrow from the JSON string, then in order to not access freed memory, the Model must not outlive the string, that is, the string must outlive the Model.

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.