Alternative to using serde internals?

While making our Deserialize, we found the following to work quite well:

impl<'de, T: Deserialize<'de>> Deserialize<'de> for MayBe<T> {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let mut depth: usize = 0;
        let content = match <serde::__private::de::Content as serde::Deserialize>::deserialize(d) {
            Ok(val) => val,
            Err(e) => {
                return Err(e);
            }
        };
        match Deserialize::deserialize(MayBeDer(&mut depth, serde::__private::de::ContentRefDeserializer::<D::Error>::new(&content))) {
            Ok(t) => Ok(MayBe::Is(t)),
            Err(_) if depth == 0 => Ok(MayBe::IsNot),
            Err(e) => Err(e),
        }
    }
}

However, this relies on internals of how untagged enums work in serde. Unfortunately we don't seem to be able to make this work with #[serde(deserialize_with)] because that requires a T and not a MayBe<T>. Any ideas for how to make this work without serde internals, and ideally without writing another two Deserializer impls and another Visitor?

What does it do?

2 Likes

The whole thing (including the two Deserializers and the Visitor) manages to shallowly match the types, akin to datafu (own crate).

The second deserializer is key here - it's what tells whether the visitor succeeded. It only becomes relevant for deeper types, like struct fields.

After sleeping for a bit, we think we can do something with a deserialize-from and a wrapper enum. Haven't tried it yet tho.

Edit: Indeed, this worked

#[derive(Deserialize)]
#[serde(untagged)]
enum MayBeHelper<T> {
    #[serde(deserialize_with="may_be")]
    #[serde(bound = "T: Deserialize<'de>")]
    Value(MayBe<T>),
}

fn may_be<'de, D: Deserializer<'de>, T: Deserialize<'de>>(d: D) -> Result<MayBe<T>, D::Error> {
    let mut depth: usize = 0;
    match T::deserialize(MayBeDer(&mut depth, d)) {
        Ok(t) => Ok(MayBe::Is(t)),
        Err(_) if depth == 0 => Ok(MayBe::IsNot),
        Err(e) => Err(e),
    }
}

impl<'de, T: Deserialize<'de>> Deserialize<'de> for MayBe<T> {
    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        MayBeHelper::<T>::deserialize(d).map(|MayBeHelper::Value(v): _| v)
    }
}

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.