Can I rely on the serialization of two identical structures being identical?

Viewed 126

Consider the following code:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct MyStruct {
   a: u32,
   b: u32
}

#[derive(Serialize, Deserialize)]
#[serde(rename = "MyStruct")]
struct AlsoMyStruct {
   a: u32,
   b: u32
}

I am wondering if I can safely do something like:

let ser = any_encoding::serialize(&MyStruct{a: 33, b: 44}).unwrap();
let deser: AlsoMyStruct = any_encoding::deserialize(&ser).unwrap();

where any_encoding is, e.g., bincode, json, or any other Serde-supported encoding. In my head, this should work nicely: the two structures have the same name (I'm explicitly renaming AlsoMyStruct into "MyStruct") and exactly the same fields: same field names, same field types, same order of fields.

However, I am wondering: is this is actually guaranteed to work? Or is there some other, corner-case, maybe platform-dependent, unforeseen piece of information that a Serde serializer/deserializer might include in the representation of MyStruct / AlsoMyStruct that could lead to the two representations being incompatible?

1 Answers

In general, no, you cannot expect this to work. The reason is that neither serde nor any de/serializers guarantee that you can round-trip your data (source). This means you cannot even expect this to work in all cases if you use the same struct in both places.

For example JSON cannot round-trip Option<()> and formats which are not self-describing like bincode, do not support untagged enums. Nothing in the type signatures enforces round-tripping.
Here are some reasons why deserialization might fail:

  • Using skip_serializing_none with not self-describing formats (serde #1732).
  • Anything which calls deserialize_any, such as untagged, adjacently tagged, or internally tagged enums (serde #1762).
  • Borrowing during deserialization, e.g., for &'de str or &'de [u8]. serde_json only supports &'de str if there are no escape sequences and never supports &'de [u8].
  • Some formats cannot serialize some types, e.g., JSON does not supports structs as map keys and bincode only supports sequences of known lengths (bincode #167).
  • A type only implements one of the traits (Serializer/Deserializer) or the implementations do not match, e.g., serialize as number but deserialize as string.

That being said, this can work under some circumstances. The structs should have the same name and the fields in the same order. The types or rather the Serialize/Deserialize implementations also need to support round-tripping. With Option<()> from above it also depends on the Serializer/Deserializer implementations if you can round-trip them, even if Serialize/Deserialize implementations do support it.

Many types do try to support round-tripping, since that is what most commonly is expected.

Related