Serde deserialization of a generic struct

Hi,

I am trying to creatue a structure with a geneics that I want to Serialize and Deserialize with Serde.

Here is how is define my struct:

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Request<T> 
where
T: Serialize + Deserialize
{
    pub id: String,
    pub message: T,
}

impl<T> Request<T> 
where
    T: Serialize + Deserialize,
{
    pub fn new(id: impl ToString, message: T) -> Request<T> {
        Request {
            id: id.to_string(),
            message,
        }
    }

    pub fn to_serialized_string(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(&self)
    }

    pub fn serialize_to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
        serde_json::to_vec(&self)
    }
}

I want to use such structure with generic type like String or even some enum defined like this :

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
pub enum DomainApiMessageAnswer {
    Starting,
    Stoping,
    Restarting,
    ReloadConfiguration,
    Error
}

And I have the following error :

error[E0106]: missing lifetime specifier
  --> unixsocket/src/message.rs:16:20
   |
16 |     T: Serialize + Deserialize,
   |                    ^^^^^^^^^^^ expected named lifetime parameter
   |
help: consider making the bound lifetime-generic with a new `'a` lifetime
   |
16 |     T: Serialize + for<'a> Deserialize<'a>,
   |                    +++++++            ++++
help: consider making the bound lifetime-generic with a new `'a` lifetime
   |
16 |     for<'a> T: Serialize + Deserialize<'a>,
   |     +++++++                           ++++
help: consider introducing a named lifetime parameter
   |
14 ~ impl<'a, T> Request<T> 
15 | where
16 ~     T: Serialize + Deserialize<'a>,
   |

At first I though that not putting the where clause on the generics would suffice because the enum can be Deserialized but with no chances.

How can I solve that?

As long as you don't want to support non-'static T (your example enum and String are both 'static), you can follow the help messages of your compiler error and your snippet compiles fine. You could also replace the Deserialize bound with DeserializeOwned to get rid of the lifetime alltogether (DeserializeOwned is basically syntactic sugar for for<'a> Deserialize<'a>).

1 Like

I tested also with DeserializedOwned also like this :

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Request<T> 
where
T: Serialize + DeserializeOwned
{
    pub id: String,
    pub message: T,
}

impl<T> Request<T> 
where
    T: Serialize + DeserializeOwned,
{
    pub fn new(id: impl ToString, message: T) -> Request<T> {
        Request {
            id: id.to_string(),
            message,
        }
    }

    pub fn to_serialized_string(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(&self)
    }

    pub fn serialize_to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
        serde_json::to_vec(&self)
    }
}

but when using the struct like this : serde_json::from_str::<Request<String>>(&stringified_request)

I get the following error:

error[E0277]: the trait bound `message::Request<std::string::String>: Deserialize<'_>` is not satisfied
    --> unixsocket/src/message.rs:86:13
     |
86   |             serde_json::from_str::<Request<String>>(&stringified_request).unwrap(),
     |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `message::Request<std::string::String>`
     |
     = help: the following other types implement trait `Deserialize<'de>`:
               bool
               char
               isize
               i8
               i16
               i32
               i64
               i128
             and 145 others
note: required by a bound in `serde_json::from_str`
    --> /home/jf/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.107/src/de.rs:2675:8
     |
2673 | pub fn from_str<'a, T>(s: &'a str) -> Result<T>
     |        -------- required by a bound in this function
2674 | where
2675 |     T: de::Deserialize<'a>,
     |        ^^^^^^^^^^^^^^^^^^^ required by this bound in `from_str`

Well, you haven't actually implemented Deserialize for Request. Here an example implementation.

Never mind the example I gave above with the manual implementation. Just remove the trait bounds on T from your struct (which is the idiomatic way to do it to begin with—add bounds in your trait implementations and impl blocks instead of on the struct directly) and serde adds the right ones automatically for you, so you can actually use the Deserialize derive macro:

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Request<T> {
    pub id: String,
    pub message: T,
}

impl<T> Request<T>
where
    T: Serialize,
{
    pub fn new(id: impl ToString, message: T) -> Request<T> {
        Request {
            id: id.to_string(),
            message,
        }
    }

    pub fn to_serialized_string(&self) -> Result<String, serde_json::Error> {
        serde_json::to_string(&self)
    }

    pub fn serialize_to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
        serde_json::to_vec(&self)
    }
}

fn main() {
    let stringified_request = r#"{
        "id": "foo",
        "message": "bar"
    }"#;

    let res = serde_json::from_str::<Request<String>>(&stringified_request).unwrap();

    assert_eq!(
        res,
        Request {
            id: "foo".to_owned(),
            message: "bar".to_owned()
        }
    );
}

Playground.