0

In rust there is a constraint on template type parameters forcing to have usage of the parameter in each template argument should be used in the scope of structure.

struct S1<'a, T> where T: Deserialize<'a>
{
    pub data: T
}
    
impl<'a, T> S1<'a, T> {
    fn update(&mut self, str_json: String) {
        self.data = serve_json::from_str(&str_json).unwrap();
    } 
}

In the above mentioned code serve_json requires the type to implement deserialiser with lifetime parameter, but the structure doesn't directly use it for the underling members. what is the best way to overcome the problem?

Martin Ayvazyan
  • 429
  • 1
  • 5
  • 16

2 Answers2

2

The simplest way is to not bound T on S1 at all, only on the implementation:

struct S1<T> {
    pub data: T
}
    
impl<'a, T> S1<T> where T: Deserialize<'a> {
    // ...
}

Further reading:

cdhowie
  • 158,093
  • 24
  • 286
  • 300
0

I take into consideration the answer of @cdhowie and try to make the example work entirely. To do so, we have to ensure the lifetime of the incoming string (str_json) is compatible with the lifetime of Deserialize<'a>.

With a String parameter, it is not the case: it will disappear when the function ends. Then, we need a &str and explicitly associate its lifetime with the one of Deserialize<'a>.

This page explains quite well why the lifetime on Deserialize<'a> is important: it enables zero-copy extractions (when suitable).

In order to illustrate this, the example below first deals with a struct which owns its data (a String) and then a second struct which references its data (a &str).

According to the same page, DeserializedOwned is to be considered when we cannot guaranty that the incoming string to be parsed will live longer than the extracted data. In this case, we can get back to the situation where the parameter is a String.

use serde::{de::DeserializeOwned, Deserialize};

#[derive(Debug)]
struct S1<T> {
    pub data: T,
}

impl<'a, T> S1<T>
where
    T: Deserialize<'a>,
{
    fn update_from_str(
        &mut self,
        str_json: &'a str,
    ) {
        self.data = serde_json::from_str(str_json).unwrap();
    }
}

impl<T> S1<T>
where
    T: DeserializeOwned,
{
    fn update_from_string(
        &mut self,
        str_json: String,
    ) {
        self.data = serde_json::from_str(&str_json).unwrap();
    }
}

fn main() {
    let mut owned = S1 {
        data: "one".to_owned(),
    };
    println!("owned before {:?}", owned);
    owned.update_from_str(" \"two\" ");
    println!("owned after {:?}", owned);
    //
    let mut referenced = S1 { data: "three" };
    println!("referenced before {:?}", referenced);
    referenced.update_from_str(" \"four\" ");
    println!("referenced after {:?}", referenced);
    //
    let mut owned_and_string = S1 {
        data: "five".to_owned(),
    };
    println!("owned_and_string before {:?}", owned_and_string);
    owned_and_string.update_from_string(" \"six\" ".to_owned());
    println!("owned_and_string after {:?}", owned_and_string);
}
/*
owned before S1 { data: "one" }
owned after S1 { data: "two" }
referenced before S1 { data: "three" }
referenced after S1 { data: "four" }
owned_and_string before S1 { data: "five" }
owned_and_string after S1 { data: "six" }
*/
prog-fh
  • 13,492
  • 1
  • 15
  • 30
  • and what if the str is not static, i.e. str_json is created instead the function or some other function in 'implementation' block? – Martin Ayvazyan Jun 18 '22 at 18:48
  • @MartinAyvazyan I'm not certain I fully understand your last comment, but I tried to elaborate on the case of an owned string to be parsed. – prog-fh Jun 18 '22 at 19:10