How to require a nested struct to implemet Deserialize trait?

I have a struct that contains a nested generic struct and looks like this:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Command<P> where P: Serialize {
    command: String,
    params: P,
}

impl<P: Serialize> Command<P> {
    pub fn new(command: &str, params: P) -> Self {
        Self {
            command: command.to_owned(),
            params
        }
    }
}

The nested struct needs to implement the Serialize and Deserialize traits too. As you can see, I can require that P implements the Serialize trait without problem, but I'm struggeling to add a similar requirement for the Deserialize trait :sweat_smile:


As soon as I add:

pub struct Command<P> where P: Serialize + Deserialize {

I get this error:

expected named lifetime parameter
help: consider making the bound lifetime-generic with a new `'a` lifetime

But this won't work either:

pub struct Command<'a, P> where P: Serialize + Deserialize<'a> {

...because of:

error[E0283]: type annotations needed: cannot satisfy `P: Deserialize<'_>`

Also, why does my "outer" struct suddenly need a life-time parameter when it didn't before?

Actually I don't want the "outer" struct to have that additional life-time parameter, if possible...

Any ideas?

Thank you and best regatrds.

You probably want to use DeserializeOwned rather than Deserialize.

2 Likes

Actually I tried that:

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

#[derive(Serialize, Deserialize, Debug)]
pub struct Command<P> where P: Serialize + DeserializeOwned {
    command: String,
    params: P,
}

impl<P: Serialize> Command<P> {
    pub fn new(command: &str, params: P) -> Self {
        Self {
            command: command.to_owned(),
            params
        }
    }
}

But then I get:

error[E0277]: the trait bound `P: DeserializeOwned` is not satisfied
the trait `for<'de> Deserialize<'de>` is not implemented for `P`
note: required for `P` to implement `DeserializeOwned`

:confused:

Don't add where on the struct. The derive already adds the necessary bounds itself.

You only need the generic bound on functions using the deserialize functionality.

1 Like

Interesting.

But then how do I prevent my "outer" (generic) struct to be instantiated with an "inner" sturct that doesn't impelemnt Serialize or that doesn't implement Deserialize?

I think, without that check, my "outer" struct may suddenly fail (for not so obvisous reasons) when one attempts to pass it to a function that requires the deserialize functionality...

You don't prevent it at creation time, but it won't compile as soon as it's used in any code with a Deserialize bound. This is till a compile-time guarantee.

This is generally the approach taken by types in Rust. Otherwise the generic bounds need to be repeated in many places that don't really need them.

3 Likes

Okay. Thank you for clarification.

Now that you've gotten the answer to your specific program, let's answer the syntax problem: Use a comma instead of a plus sign.

struct ... where P: Serialize, P: Deserialize