Bincode does not support the serde::Deserializer::deserialize_any method
主线位置:两条主线和缓冲主线在非自描述格式上的交点。bincode、postcard 的字节里没有类型标记,反序列化只能照类型给的提示一步步读;缓冲主线要的
deserialize_any它们给不了。序列化一侧,它们要求事先知道长度,也完全依赖「写了什么、就按同样的顺序读什么」。
flowchart TB
ty["一个 serde 类型"] --> q1{"要 deserialize_any 吗"}
q1 -->|"内部标签 untagged Value"| e1["读不回"]
q1 -->|否| q2{"长度事先知道吗"}
q2 -->|"flatten collect_seq"| e2["写不出"]
q2 -->|是| q3{"写读字段一致吗"}
q3 -->|skip_serializing_if| e3["读错位"]
q3 -->|"skip_deserializing 变体"| e4["序号错一位"]
q3 -->|是| ok["往返成功"]
为什么 bincode 答不了「下一个值是什么」,在 serde 解决什么问题 的「自描述格式与非自描述格式」;各种表示怎么用到 deserialize_any,在 enum 的表示与缓冲 这一部分;len 的约定在 Serialize 与 Serializer。这一页把这些散在各处的结论放进一张表,每一格都是实测。
本页实验在 M2 Max、macOS、rustc 1.99.0 上运行,依赖 serde = "=1.0.229"(derive)、serde_json = "=1.0.154"、bincode = "=1.3.3"、postcard = "1"(解析到 1.1.3,开 alloc)。bincode 用 bincode::serialize / bincode::deserialize 的默认配置,postcard 用 to_allocvec / from_bytes。
现象
src/bin/binary.rs:每个类型构造一个值,分别用 bincode、postcard、serde_json 写出再读回,比较读回的值和原值。
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(tag = "t")]
enum Internal {
Rec { id: u32 },
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct SkipIf {
#[serde(skip_serializing_if = "Option::is_none")]
note: Option<String>,
id: u32,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Gap {
A,
#[serde(skip_deserializing)]
B,
C,
}
// Serialize only the even numbers: the iterator does not know its length.
#[derive(Deserialize, Debug, PartialEq)]
struct Evens(Vec<u32>);
impl Serialize for Evens {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.collect_seq(self.0.iter().filter(|n| *n % 2 == 0))
}
}
// ... Plain、External、Adjacent、Untagged、Flat、Skip、GapLast、WithValue 同理
fn cell<E: std::fmt::Display, T: Debug + PartialEq>(orig: &T, ser: Result<Vec<u8>, E>, de: impl Fn(&[u8]) -> Result<T, String>) -> String {
match ser {
Err(e) => format!("ser Err({e})"),
Ok(bytes) => match de(&bytes) {
Ok(back) if &back == orig => format!("{bytes:?} -> same"),
Ok(back) => format!("{bytes:?} -> DIFFERENT {back:?}"),
Err(e) => format!("{bytes:?} -> de Err({e})"),
},
}
}
fn row<T: Serialize + DeserializeOwned + Debug + PartialEq>(label: &str, v: T) {
println!("{label}: {v:?}");
let b = cell(&v, bincode::serialize(&v), |b| bincode::deserialize::<T>(b).map_err(|e| e.to_string()));
println!(" bincode {b}");
let p = cell(&v, postcard::to_allocvec(&v), |b| postcard::from_bytes::<T>(b).map_err(|e| e.to_string()));
println!(" postcard {p}");
// ... serde_json 同样往返,打印 JSON 文本
}
输出(跑 3 次相同):
plain struct: Plain { id: 7, name: "a" }
bincode [7, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 97] -> same
postcard [7, 1, 97] -> same
json {"id":7,"name":"a"} -> same
external unit: Unit
bincode [0, 0, 0, 0] -> same
postcard [0] -> same
json "Unit" -> same
external newtype: New(7)
bincode [1, 0, 0, 0, 7, 0, 0, 0] -> same
postcard [1, 7] -> same
json {"New":7} -> same
external struct: Rec { id: 7 }
bincode [2, 0, 0, 0, 7, 0, 0, 0] -> same
postcard [2, 7] -> same
json {"Rec":{"id":7}} -> same
internal: Rec { id: 7 }
bincode [3, 0, 0, 0, 0, 0, 0, 0, 82, 101, 99, 7, 0, 0, 0] -> de Err(Bincode does not support the serde::Deserializer::deserialize_any method)
postcard [3, 82, 101, 99, 7] -> de Err(This is a feature that PostCard will never implement)
json {"t":"Rec","id":7} -> same
adjacent: New(7)
bincode [0, 0, 0, 0, 7, 0, 0, 0] -> de Err(Bincode does not support Deserializer::deserialize_identifier)
postcard [0, 7] -> de Err(This is a feature that PostCard will never implement)
json {"t":"New","c":7} -> same
untagged: New(7)
bincode [7, 0, 0, 0] -> de Err(Bincode does not support the serde::Deserializer::deserialize_any method)
postcard [7] -> de Err(This is a feature that PostCard will never implement)
json 7 -> same
flatten: Flat { id: 7, inner: Inner { name: "a" } }
bincode ser Err(Bincode can only encode sequences and maps that have a knowable size ahead of time)
postcard ser Err(The length of a sequence must be known)
json {"id":7,"name":"a"} -> same
skip_serializing_if, None: SkipIf { note: None, id: 7 }
bincode [7, 0, 0, 0] -> de Err(tag for enum is not valid, found 7)
postcard [7] -> de Err(Found an Option discriminant that wasn't 0 or 1)
json {"id":7} -> same
skip_serializing_if, None, id 1: SkipIf { note: None, id: 1 }
bincode [1, 0, 0, 0] -> de Err(io error: unexpected end of file)
postcard [1] -> de Err(Hit the end of buffer, expected more data)
json {"id":1} -> same
skip_serializing_if, Some: SkipIf { note: Some("a"), id: 7 }
bincode [1, 1, 0, 0, 0, 0, 0, 0, 0, 97, 7, 0, 0, 0] -> same
postcard [1, 1, 97, 7] -> same
json {"note":"a","id":7} -> same
skip: Skip { cache: None, id: 7 }
bincode [7, 0, 0, 0] -> same
postcard [7] -> same
json {"id":7} -> same
skip_deserializing variant, A: A
bincode [0, 0, 0, 0] -> same
postcard [0] -> same
json "A" -> same
skip_deserializing variant, B: B
bincode [1, 0, 0, 0] -> DIFFERENT C
postcard [1] -> DIFFERENT C
json "B" -> de Err(unknown variant `B`, expected `A` or `C` at line 1 column 3)
skip_deserializing variant, C: C
bincode [2, 0, 0, 0] -> de Err(invalid value: integer `2`, expected variant index 0 <= i < 2)
postcard [2] -> de Err(Serde Deserialization Error)
json "C" -> same
skipped variant last, C: C
bincode [1, 0, 0, 0] -> same
postcard [1] -> same
json "C" -> same
skipped variant last, B: B
bincode [2, 0, 0, 0] -> de Err(invalid value: integer `2`, expected variant index 0 <= i < 2)
postcard [2] -> de Err(Serde Deserialization Error)
json "B" -> de Err(unknown variant `B`, expected `A` or `C` at line 1 column 3)
collect_seq(filter): Evens([2, 4])
bincode ser Err(Bincode can only encode sequences and maps that have a knowable size ahead of time)
postcard ser Err(The length of a sequence must be known)
json [2,4] -> same
serde_json::Value: WithValue { v: Number(7) }
bincode [7, 0, 0, 0, 0, 0, 0, 0] -> de Err(Bincode does not support the serde::Deserializer::deserialize_any method)
postcard [7] -> de Err(This is a feature that PostCard will never implement)
json {"v":7} -> same
一张表
「往返」指写出成功、读回的值和原值相等。
| 类型 / 属性 | bincode 1.3.3 | postcard 1.1.3 | 卡在哪 |
|---|---|---|---|
| 普通 struct | 往返 | 往返 | — |
| 外部标签 enum(unit / newtype / struct 变体) | 往返 | 往返 | — |
内部标签 tag = "t" | 能写,读:deserialize_any 不支持 | 能写,读:WontImplement | TaggedContentVisitor 要 deserialize_any |
相邻标签 tag, content | 能写,读:deserialize_identifier 不支持 | 能写,读:WontImplement | 生成的 visit_seq 用 __Field 读标签 |
untagged | 能写,读:deserialize_any 不支持 | 能写,读:WontImplement | 先缓冲成 Content |
字段 serde_json::Value | 能写,读:deserialize_any 不支持 | 能写,读:WontImplement | Value 只能 deserialize_any |
flatten | 写:要求事先知道长度 | 写:The length of a sequence must be known | derive 调 serialize_map(None) |
collect_seq 一个 filter 迭代器 | 写:要求事先知道长度 | 写:同上 | serialize_seq(None) |
skip_serializing_if,值被跳过 | 能写,读:错位(tag for enum is not valid, found 7 / EOF) | 能写,读:错位(Option discriminant / EOF) | 写少了一个字段,读按全部字段 |
skip_serializing_if,值没跳过 | 往返 | 往返 | — |
#[serde(skip)] 字段 | 往返 | 往返 | 写读两边都跳过 |
skip_deserializing 变体之前的变体 | 往返 | 往返 | — |
skip_deserializing 变体本身 | 读成下一个变体,不报错 | 读成下一个变体,不报错 | 读的一侧序号重排 |
skip_deserializing 变体之后的 C(最后一个) | 能写,读:variant index 0 <= i < 2 越界 | 能写,读:Serde Deserialization Error | 同上 |
skip_deserializing 变体放在最后 | 其他变体往返;它自己读:越界报错 | 同左,报错没有原文 | 前面的序号不变 |
JSON 那一列除了 skip_deserializing 的变体(unknown variant \B``,这是该有的)全部往返成功。下面按「卡在哪」分四类说。
出处
要 deserialize_any 的:内部标签、untagged、Value
bincode 1.3.3 的 deserialize_any 只有一行 Err(Box::new(ErrorKind::DeserializeAnyNotSupported)),这个 ErrorKind 的显示文本就是本页标题;postcard 1.1.3 的 deserialize_any、deserialize_identifier、deserialize_ignored_any 都返回 Error::WontImplement,显示为「This is a feature that PostCard will never implement」。内部标签和 untagged 都要先把整段输入读成 Content,serde_json::Value 本身就是「读到什么算什么」,三者都只能问格式「这里是什么」,见 Content。
写出没有问题:序列化一侧是类型推数据给格式,内部标签就是 serialize_struct 多写一个字段。bincode 的 [3, 0, 0, 0, 0, 0, 0, 0, 82, 101, 99, 7, 0, 0, 0] 是长度 3 的字符串 "Rec" 加 u32 的 7;读的一方不知道开头 8 个字节是一个长度,这些字节也没告诉它。
相邻标签不走 deserialize_any,卡在另一处:bincode 把 struct 当元组读,生成的 visit_seq 读第一个元素(标签)要经 __Field 的 deserialize_identifier,bincode 不支持(「Bincode does not support Deserializer::deserialize_identifier」),postcard 同样返回 WontImplement。细节在 内部标签与相邻标签 的「相邻标签与 bincode」。
长度未知的:flatten、filter 迭代器
二进制格式没有结束符,序列的长度要写在元素前面,bincode 1.3.3 的 serialize_seq 第一行就是 len.ok_or(ErrorKind::SequenceMustHaveLength)?。有 flatten 字段的 struct,derive 改调 serialize_map(None);collect_seq 只在迭代器的 size_hint 上下界相等时才传 Some(n),filter 的下界是 0。两者在第一个字节写出之前就失败。机制见 Serialize 与 Serializer 的「谁传 None」和 flatten。
写读不一致的:skip_serializing_if
SkipIf { note: None, id: 7 } 在 bincode 里写成 [7, 0, 0, 0]:note 被跳过,只剩 id。读的时候 bincode 的 deserialize_struct 是 self.deserialize_tuple(fields.len(), visitor),按 FIELDS 的个数读元组;derive 的 deserialize_seq 生成的 visit_seq 按声明顺序一个个 next_element,第一个要的是 Option<String>。于是 7 被当成 Option 的标签字节:bincode 报「tag for enum is not valid, found 7」,postcard 报「Found an Option discriminant that wasn't 0 or 1」。id 换成 1,标签就成了 Some,接着把后面的字节当字符串长度读,读到末尾报 EOF。哪种报错都和真正的原因(少写了一个字段)对不上。
JSON 没事,是因为 JSON 写了键名:读的一方按键认字段,缺了的 Option 字段由 missing_field 补成 None(见 missing field)。#[serde(skip)] 也没事:它让字段在写、读两边都不出现,FIELDS 里没有它,位置对得上。
问: derive 给 serialize_struct 传的 len 是运行时算的,跳过的字段不计入(Serialize 与 Serializer)。bincode 拿到了 len = 1,为什么不把它写进去,读的时候就知道少了一个?
答: 因为 bincode 1.3.3 的 serialize_struct(self, _name, _len) 直接丢掉了 _len,struct 不写任何长度前缀:每个字段都写、读两边固定,是它体积小的原因之一。就算写了个数,读的一方也只知道「少了一个」,不知道少的是哪一个:bincode 不写键名,按位置认字段。要知道是哪一个,得在每个字段前写下标或名字,那就是另一种格式了。
serde 其实给格式留了一个钩子。derive 在跳过字段时会调 SerializeStruct::skip_field,默认实现什么也不做:
#[inline]
fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
let _ = key;
Ok(())
}
bincode 1.3.3 和 postcard 1.1.3 的源码里都没有出现 skip_field,用的是这个空实现。
skip_field 是为一种「按整数键写字段」的格式加的issue #960「Pass field index to SerializeStruct::serialize_field」提出:「This would enable CBOR to deserialize correctly from a packed representation (integer keys) with fields that are conditionally skipped」。CBOR 的紧凑表示用字段下标代替键名,可是 serialize_field 只给名字,格式要自己数第几个字段;有字段被 skip_serializing_if 跳过时,格式根本不知道,数出来的下标就错了。
PR #1022「Inform serializers about skipped fields.」没有改 serialize_field 的签名(那会破坏所有已有的格式),而是加了一个带默认空实现的 skip_field,derive 在跳过时调用它,格式可以借此把计数加一。它首次发布在 1.0.13,发布说明写的是「Add an optional skip_field method so serializers can be notified when a field is skipped (#1022)」。
现在这个钩子还在,但 bincode 1.3.3、postcard 1.1.3 都没有用它:它们按位置写字段,skip_field 能告诉它们「这里少了一个」,却没有地方把这件事写进字节里。
序号重排的:skip_deserializing 变体
Gap { A, #[serde(skip_deserializing)] B, C } 写出时 B 的序号是 1、C 是 2,展开里是 serialize_unit_variant(__serializer, "Gap", 2u32, "C")(外部标签 里有完整的展开)。读的一侧先滤掉 skip_deserializing 的变体,再给剩下的重新编号:u64_mapping 是 deserialized_fields.iter().enumerate(),A 是 0,C 成了 1。
let u64_mapping = deserialized_fields.iter().enumerate().map(|(i, field)| {
let i = i as u64;
let ident = &field.ident;
quote!(#i => _serde::#private::Ok(#this_value::#ident))
});
于是:
B写出的 1 读成了C,不报错。 这是表里唯一一个「读成功但读错」的格子。本该被拒绝的变体,静静地变成了另一个。C写出的 2 越界。 它是最后一个变体,没有「下一个」可读;issue #2614 里的例子是A、#[serde(skip)] B、C、D(skip包含skip_deserializing),C在 bincode 和 postcard 上都读成了D。bincode 报invalid value: integer \2`, expected variant index 0 <= i < 2;postcard 的de::Error::custom` 丢掉了消息,只剩「Serde Deserialization Error」。- 把被跳过的变体放到最后,前面的序号就不受影响(
GapLast),只有它自己越界报错。
这是开着的 issue #2614「Binary serialize/deserialize inconsistency of enums with skipped fields」,写作时 1.0.229 仍是这个行为。JSON 按名字认变体,不受影响。
修法
- 给二进制格式用外部标签。 四种表示里只有它在这两个格式上能往返。同一个类型既要发 JSON(想要内部标签)又要存 bincode 时,分成两个类型,用
From互转,比在一个类型上兼顾两种格式可靠。 - 不用 flatten,不
collect_seq一个长度未知的迭代器。 前者把字段直接写在 struct 里;后者先collect成Vec,或者用ExactSizeIterator。 skip_serializing_if只留给 JSON 一类按键名读的格式。 给二进制格式用的类型去掉它;字段确实不需要传输,用#[serde(skip)],写读两边一致。skip_deserializing的变体放到 enum 的最后,或者干脆不用。不能挪的,至少别让它出现在要读回来的数据里。- 字段别用
serde_json::Value,换成具体的类型或外部标签的 enum。
怎么确认没中招
对每个会写进二进制格式的类型,每个变体、每个 Option 字段的 None 和 Some,都做一次往返断言:
fn roundtrip<T: Serialize + DeserializeOwned + PartialEq + std::fmt::Debug>(v: &T) {
let bytes = bincode::serialize(v).unwrap();
assert_eq!(&bincode::deserialize::<T>(&bytes).unwrap(), v);
let bytes = postcard::to_allocvec(v).unwrap();
assert_eq!(&postcard::from_bytes::<T>(&bytes).unwrap(), v);
}
一定要比较读回的值,不能只看有没有报错:Gap::B 读成 Gap::C 时 deserialize 返回的是 Ok。也不要拿 JSON 的往返测试代替:上面的表里 JSON 一列几乎全绿,问题只在二进制格式上出现。
小结
- bincode 1.3.3、postcard 1.1.3 不支持
deserialize_any(postcard 连deserialize_identifier也不支持):内部标签、untagged、serde_json::Value能写不能读,相邻标签同样读不回。外部标签是唯一能往返的 enum 表示。 - 长度必须事先知道:flatten(
serialize_map(None))和filter迭代器的collect_seq在写出第一步就失败。 skip_serializing_if跳过字段后,按位置读的格式整体错位,报错和原因无关;#[serde(skip)]两边一致,没事。serde 1.0.13 起有skip_field钩子(PR #1022),这两个格式都没用。skip_deserializing变体让读的一侧重新编号:它自己读成下一个变体且不报错,它后面的变体也跟着错位,最后一个越界报错(issue #2614,未修)。放到最后可以保住其他变体。- 防线是对每个变体、每种
Option状态做二进制往返并比较值。