Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a generic serde_json::from_str [duplicate]

Tags:

json

rust

serde

I am trying to write a generic code that reads a json file into an object. But it seems that there is something I am missing here.

use serde::Deserialize;
use std::{error::Error, fs::File, io::Read};

pub fn from_file<'a, T>(filename: String) -> Result<T, Box<dyn Error>>
where
    T: Deserialize<'a>,
{
    let mut file = File::open(filename)?;
    let mut content: String = String::new();
    file.read_to_string(&mut content)?;
    Ok(serde_json::from_str::<T>(&content)?)
}

I am getting the following error

error[E0597]: `content` does not live long enough
  --> src\util\file.rs:11:34
   |
4  | pub fn from_file<'a, T>(filename: String) -> Result<T, Box<dyn Error>>
   |                  -- lifetime `'a` defined here
...
11 |     Ok(serde_json::from_str::<T>(&content)?)
   |        --------------------------^^^^^^^^-
   |        |                         |
   |        |                         borrowed value does not live long enough
   |        argument requires that `content` is borrowed for `'a`
12 | }
   | - `content` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.
error: could not compile `flavour` due to previous error

What I understand is that I have to bound T, thanks for this SO question. But I am uncertain which bounds to add.

And how to infer the required bounds for such problems. I tried to read from_str and found that it requires T: de::Deserialize<'a> only.

like image 770
Jaafar Avatar asked Mar 22 '26 06:03

Jaafar


1 Answers

The lifetime argument to Deserialize<'de> indicates the lifetime of the data you're deserializing from. For efficiency reasons, serde is allowed to borrow data straight from the structure you're reading from, so for example if your JSON file contained a string and the corresponding Rust structure contained a &str, then serde would borrow the string directly from the JSON body. That means that the JSON body, at least for Deserialize, has to live at least as long as the structure deserialized from it, and that length of time is captured by the 'de lifetime variable (called 'a in your example).

If you want to read data without borrowing anything from it, you're looking for DeserializeOwned. From the docs,

Trait serde::de::DeserializeOwned

A data structure that can be deserialized without borrowing any data from the deserializer.

This is primarily useful for trait bounds on functions. For example a from_str function may be able to deserialize a data structure that borrows from the input string, but a from_reader function may only deserialize owned data.

So if your bound is DeserializeOwned (which is really just for<'de> Deserialize<'de>), then you can read data from the string without borrowing anything from it. In fact, since you're reading from a file, you can use from_reader, which does so directly, and you don't even have to worry about the intermediate string at all.

like image 174
Silvio Mayolo Avatar answered Mar 24 '26 19:03

Silvio Mayolo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!