Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Am I passing metadata through DeserializeSeed to facilitate deserialization of algebraic datatypes as intended #1132

Closed
hirenhpatel opened this issue May 12, 2024 · 2 comments

Comments

@hirenhpatel
Copy link

hirenhpatel commented May 12, 2024

I have data types that need to be deserialized from json that look like this:

// inlining inner Node struct to better illustrate structure of data-tree:

struct MyData {
    field1: Dynamic<(Float, String, Node {fieldA: Scalar<Int>,  ...} )>,
    field2: Scalar<String>,
}

The datatypes that appear are quite varied, and as a result, I am essentially writing my own implementations of Deserialize.

I have now reached a situation where I need extra information (let's call it MyMetadata) to properly deserialize the Node struct.

Happily, I found DeserializeSeed and started to rewrite all my Deserialize implementations to DeserializeSeed implementations since I can then utilize the extra seed to argument pipe MyMetadata through deserialization. My thought here is MyMetadata tags along for the ride remaining unused, until a Node is encountered by the Visitor at which point I can use it to facilitate deserialization.

Unfortunately, I've run into an awkward problem: it seems that I'm not supposed to implement DeserializeSeed on Dynamic, Scalar, Float like I am supposed to in Deserialize. Rather, I'm supposed to implement it on a new data structure, and the associated return type is supposed to be one of Dynamic, Scalar, Float. With that interface, it's not clear to me how I should be using DeserializeSeed to move the MyMetadata through the deserialization process?

Should I add an unused generic parameter on MyMetadata and implement DeserializeSeed on the various concrete types, converting them to other concrete types in my implementation of Visitor?

@dtolnay
Copy link
Member

dtolnay commented May 12, 2024

Usually you would implement DeserializeSeed on the same type that also has the corresponding Visitor impl. So a canonical impl would look like:

impl<'de> DeserializeSeed<'de> for TheVisitor {
    type Value = <Self as Visitor<'de>>::Value;

    fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_………(self)
    }
}

@hirenhpatel
Copy link
Author

hirenhpatel commented May 14, 2024

The thought of implementing DeserializeSeed on Visitor never occurred to me! After studying your suggestion some more, I have settled on the pattern of having a surrogate nested visitor type DynamicVisitor<TupleVisitor<..., NodeVisitor<...>> that mirrors the target deserialization datatype: Dynamic<(..., NodeVisitor<...>)>.

I have implemented DeserializeSeed<'de> on all of the *Visitor structs, which have the metadata as needed. Then, most of the my deserialize calls look like this
deserializer.deserialize_*(self.0) to pass the inner visitor to the next step in the recursion.

Have I understand your guidance correctly?

Further, do I understand correctly that, in my recursive deserialization implementations, I should
not mix Deserialize with DeserializeSeed? That is, in any chain, I will be dealing with Deserialize<'de> exclusively, or with DeserializeSeed<'de> exclusively?

Thanks in advance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants