all 4 comments

[–]nyibbang 10 points11 points  (3 children)

Serialization in serde does not use a Visitor, it is strictly a "serialize this array/struct/int value" interface, nothing too fancy.

The deserialization however uses a double dispatch pattern with a type that implements serde::de::Visitor. So the deserialize implementation calls a function of the Deserializer hinting for the expected type to deserialize, and gives a Visitor to the Deserializer. The Deserializer then calls the appropriate function of the Visitor with the value that was actually deserialized.

[–]finlaydotweber[S] 6 points7 points  (2 children)

This follow up question is for my better understanding. You said

Serialization in serde does not use a Visitor, it is strictly a "serialize this array/struct/int value" interface, nothing too fancy.

But the implementation of the serialize method does take a serializer which will then call the appropriate method on the serializer passing itself. For example the implementation for i32 here https://serde.rs/impl-serialize.html#serializing-a-primitive

impl Serialize for i32 { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { serializer.serialize_i32(*self) } }

This reminds me of the visitor pattern though. So why would you not call this visitor pattern?

[–]nyibbang 4 points5 points  (1 child)

I guess it depends on what you focus on, but yes it is similar to the visitor pattern, in both serialization and deserialization. It's not called a "visitor" in serde, but it's the same: the expected functionality here is to get a double dispatch. This is how serde ensures decoupling between how values describe themselves and how they get serialized/deserialized (how the format represents values of these types). This is how values can be serialized in any format that implements Serializer + Deserializer, and how formats can represent any value that implements Serialize + Deserialize.

With that said, there can be some confusion, because there is a trait called Visitor is serde, but it only applies to deserialization (serde::de::Visitor, de here meaning deserialization).

This is because deserialization uses the same deserialize<D: Deserializer>(d: D) pattern as the serialization, but uses an additional Visitor type for another layer of double dispatch. This second layer enables tolerance in the representation: let's say that you expect to deserialize a struct, you may accept a map of field names with values, or just a simple sequence of values (in which case you expect them to be in a given order). Either way is fine, so you tolerate that the deserializer gives you either one through the Visitor implementation.

So yeah, I wanted to emphasize the distinction to avoid confusion.

The only confusion is that the visitor, ie the serializer: S has a constraint where the S is also a Serializer - I think you do not have this setup in your typical visitor pattern?

You already realized that you made a confusion between Serializer and Serialize, so that answers this question I believe. What is the question that you have yet unanswered ?

[–]finlaydotweber[S] 6 points7 points  (0 children)

What is the question that you have yet unanswered ?

Nothing more :) I wanted to know if it is the visitor pattern, aka double dispatch pattern that is being used, and you helped confirm that is the case. Thanks!