Forsworn
Forsworn

Reputation: 122

Deserialize trait bound on generic struct

I want to build a parser for a generic struct by serde_json, here is the simplifed logic. And it will generate an error: "src does not live long enough".

But why this would cause a not-long-enough lifetime error?

Is there any way to fix it, or it is simply not recommended to write codes like this?

use serde_json;
use serde::Deserialize;
use std::marker::PhantomData;

#[derive(Default)]
struct Parser<'de, P:Deserialize<'de>>{
    phantom1: PhantomData<&'de ()>,
    phantom2: PhantomData<P>,
}

impl<'de, P:Deserialize<'de>> Parser<'de,P>{
    fn parse(&self, src:&'de String){
        serde_json::from_str::<P>(&src).unwrap();
    }
}

#[derive(Default)]
struct OutterParser<'de, P:Deserialize<'de>>{
    parser: Parser<'de, P>
}

impl<'de, P:Deserialize<'de>> OutterParser<'de,P>{
  fn read_and_parse(&self){
    let src = String::from(r#""""#);
    self.parser.parse(&src); 
  }
}

fn main(){
    let parser = OutterParser::<String>::default();
    parser.read_and_parse();
}

Here is the rust playground link


Edit: Thanks for the answer from Kevin. I once thought that 'de lifetime would be automatically derived to be a lifetime in the local parse function. After P struct been built from local &src, the src can be dropped.

Now I know the lifetime of function arguments would be less than 'de without evident annotation.

But if the function signature is revised to fn parse(&self, &'de str), the 'de will be further propagated upwards, which is not what I want.

Upvotes: 0

Views: 2545

Answers (2)

Forsworn
Forsworn

Reputation: 122

Found answer in another question :) How to return the result of serde_json::from_str when called with a String that will be dropped at the end of the function?

An easier workaround is to use DeserializeOwned...

use serde_json;
use serde::de::DeserializeOwned;
use std::marker::PhantomData;

#[derive(Default)]
struct Parser<P:DeserializeOwned>{
    phantom2: PhantomData<P>,
}

impl<P:DeserializeOwned> Parser<P>{
    fn parse(&self, src:&String){
        serde_json::from_str::<P>(&src).unwrap();
    }
}

#[derive(Default)]
struct OutterParser<P:DeserializeOwned>{
    parser: Parser<P>
}

impl<P:DeserializeOwned> OutterParser<P>{
  fn read_and_parse(&self){
    let src = String::from(r#""""#);
    self.parser.parse(&src); 
  }
}

fn main(){
    let parser = OutterParser::<String>::default();
    parser.read_and_parse();
}

Upvotes: 1

Kevin Reid
Kevin Reid

Reputation: 43842

P: Deserialize<'de> means that deserializing into a value of type P requires input data which must live for at least 'de.

    fn parse(&self, src:String){
        serde_json::from_str::<P>(&src).unwrap();

Instead, you provided a reference to the local variable src which will be dropped at the end of the parse function, and therefore definitely lives shorter than 'de.

In order to not have this problem, you must define parse such that it accepts a reference that is valid for 'de:

    fn parse(&self, src: &'de str) {

Upvotes: 0

Related Questions