【问题标题】:Can I rely on the serialization of two identical structures being identical?我可以依赖两个相同结构的序列化是相同的吗?
【发布时间】:2021-09-09 15:23:35
【问题描述】:

考虑以下代码:

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
}

我想知道我是否可以安全地执行以下操作:

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

其中any_encoding 是例如bincodejson 或任何其他支持 Serde 的编码。在我看来,这应该可以很好地工作:这两个结构具有相同的名称(我明确地将AlsoMyStruct 重命名为"MyStruct")和完全相同的字段:相同的字段名称、相同的字段类型、相同的字段顺序。

但是,我想知道:这实际上 保证 可以工作吗?或者是否有一些其他的、极端情况、可能依赖于平台的、不可预见的信息,Serde 序列化器/反序列化器可能包含在MyStruct / AlsoMyStruct 的表示中,这可能导致两种表示不兼容?

【问题讨论】:

  • 我不知道 bincode 是如何工作的,但是在 rust 中,这些结构不能保证是相同的,如果你想的话,你必须使用#[repr(C)]doc.rust-lang.org/nomicon/repr-rust.html
  • 取决于您选择的格式,如果格式没有排序是的,如 json,bincode 有点特殊,所以我想说它应该与 bincode 相同。
  • Remote derive 似乎相邻,如果不严格适用的话。
  • 实际上,如果我理解正确,是的,如果您的结构的 rust 定义没有改变并且具有相同的值,那么它应该是相同的序列化格式,是否订购(当然使用相同的版本),但是正如 jonasbb 所说,不能保证反序列化到完全相同的值(边缘情况)

标签: rust serde


【解决方案1】:

一般来说,不,你不能指望这会起作用。原因是 serde 和任何反序列化器都不能保证您可以往返数据 (source)。这意味着如果您在两个地方使用相同的结构,您甚至不能期望它在所有情况下都有效。

例如 JSON 不能往返 Option<()> 和像 bincode 这样不能自我描述的格式不支持未标记的枚举。 类型签名中的任何内容都不会强制执行往返。
以下是反序列化可能失败的一些原因:

  • 使用 skip_serializing_none 和非自描述格式 (serde #1732)。
  • 任何调用 deserialize_any 的内容,例如未标记、相邻标记或内部标记的枚举 (serde #1762)。
  • 在反序列化期间借用,例如,用于&'de str&'de [u8]serde_json 仅在没有转义序列的情况下支持 &'de str,并且从不支持 &'de [u8]
  • 某些格式无法序列化某些类型,例如,JSON 不支持将结构体作为映射键,而 bincode 仅支持已知长度的序列 (bincode #167)。
  • 一个类型只实现了其中一个特征 (Serializer/Deserializer) 或实现不匹配,例如,序列化为数字但反序列化为字符串。

话虽如此,这可以在某些情况下工作。结构应具有相同的名称和相同的顺序的字段。类型或者更确切地说是Serialize/Deserialize 实现也需要支持往返。使用上面的Option<()>,它还取决于Serializer/Deserializer 实现是否可以往返它们,即使Serialize/Deserialize 实现确实支持它。

许多类型确实尝试支持往返,因为这是最常见的预期。

【讨论】:

  • 这是一个非常极端的案例,非常特定于 serde_json,我不确定 OP 真正问的是什么。
  • OP 在问“这是否真的可以保证工作”,答案显然是否定的。通常它会在实践中起作用,但您也可以故意破坏它或遇到带有未标记枚举、结构扁平化或使用 skip_serializing_if 的边缘情况(打破往返二进制代码)。
  • 因为 OP 没有像你的 github 链接那样断言平等,我不认为 OP 要求什么
  • @Stargateur 我添加了一些示例,说明为什么往返可能不起作用。这不仅仅影响边缘情况或相等性检查。
  • 并没有真正改变我的观点,即 OP 不要求这样做
猜你喜欢
  • 2013-02-22
  • 1970-01-01
  • 1970-01-01
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 2016-03-01
相关资源
最近更新 更多