Derive serde `Deserialize` for generic struct where only one T is deserializable

Hi Rust Users,

I am trying to write a generic struct

use serde::*;

struct A;
struct B;

struct Setting<AB, T> {
    ab: PhantomData<AB>,
    t: T,
}

// Only for Setting<A, _>
impl<'de, T: Deserialize<'de>> Deserialize<'de> for Setting<A, T> {
    fn deserialize<D>(_: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        panic!("")
    }
}

#[derive(Deserialize)]
struct Config<AB> {
    f: Setting<AB, f64>,
}

I want to derive Deserialize only for the case Config<A>, where A implements it's own Deserialize. I still want to use Config<B> elsewhere (for example convert Config<A> to it after it was deserialized), but it itself does not have to be deserializable.

A and B are just empty type "tags" to represent states in which Config and Setting can be. Imagine Unvalidated or Validated where validation can only be done after parsing, something like that. Or imagine an Expression form of a Setting with placeholders and templates, and then Final one where placeholders in a string are filled and type converted. The rest of the code can just use the final form Config<B>, after initialization has built it from Config<A>.

This code fails to compile for understandable reasons:

error[E0277]: the trait bound `Setting<AB, f64>: _serde::Deserialize<'_>` is not satisfied
    --> src/main.rs:23:8
     |
23   |     f: Setting<AB, f64>,
     |        ^^^^^^^^^^^^^^^^ the trait `_serde::Deserialize<'_>` is not implemented for `Setting<AB, f64>`
     |
note: required by a bound in `next_element`
    --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.160/src/de/mod.rs:1729:12
     |
1729 |         T: Deserialize<'de>,
     |            ^^^^^^^^^^^^^^^^ required by this bound in `SeqAccess::next_element`
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
     |
21   | #[derive(Deserialize, Setting<AB, f64>: _serde::Deserialize<'_>)]
     |                     +++++++++++++++++++++++++++++++++++++++++++

Deserialize tries to derive the implementation for any AB, but it cannot possibly do that. serde(bound(...)) is not quite it, because there is no traits here, it's just structs. Implementing custom Deserialize for Config<A> will quickly become unfeasible to maintain as I add 50 fields there with child sub-structures that all internally use Setting<AB, ...>.

I wonder if there is still a chance for bound via some non-trivial use of traits.

Or is there a way to use bound on Config somehow to narrow down any internal Settings<AB, _>: Deserialize? Not ideal again, as if Config will start using child structures containing Settings<AB, _>, they will have to be declared in the bound too. Ok to declare annotations on their definitions, but not on definitions wherever they are used, that will be too much to maintain.

Thanks, any ideas appreciated!

I think you are asking for specialization, which currently is not possible in Rust.

You'll have to use separate structs to model your requirements. If these structs are strongly related to each other, you can group them within an enum:

enum Config {
  WithDeserialize(ConfigWithDeserialize),
  WithoutDeserialize(ConfigWithoutDeserialize)
}

struct ConfigWithDeserialize;

struct ConfigWithoutDeserialize;

You could do this...

#[derive(Deserialize)]
struct ConfigA {
    f: Setting<A, f64>,
}

impl From<ConfigA> for Config<A> {
    fn from(ca: ConfigA) -> Self {
        let ConfigA { f,  } = ca;
        Self { f, }
    }
}

#[derive(Deserialize)]
#[serde(bound = "Config<AB>: From<ConfigA>")]
#[serde(from = "ConfigA")]
struct Config<AB> {
    f: Setting<AB, f64>,
}

And macro up your definitions to create all of ConfigA, the From implementation, and Config<AB>.

1 Like

Hi, thank your reply. There are a few quite verbose solutions and that require duplication of all fields. I do know it is very simple to just declare two structs. Unfortunately as Config implies in the name it is a large multi-leveled structure. The only reason I try to work with generics is to guarantee an existence of a field foo before processing and after. But after it is a different type that is impossible to construct, unless it was created by processing phase. It is very difficult problem as it seems.

Hi, thank you for your reply too. As I stated in another thread, one thing I aim for is to avoid the duplication of f, because there cold be three layers of structs inside of the Config with 50 of those fs. The only reason I attempt to play with generics is to declare Config once and only once, and declare all it's children structures once and only once.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.