开发者生态
morning
Deser:重新思考 Rust 序列化
摘要
Deser: Rethinking Rust Serialization written on September 29, 2026 Serde is an amazing serialization library for Rust and it has been a huge reason why I felt productive with it for years. However alr...
the
with
Serde
for
has
serde_json
type
and
that
are
2026-09-30
1 阅读
约7分钟阅读
tosh
字号:
Deser:重新思考 Rust 序列化,写于 2026 年 9 月 29 日 Serde 是一个令人惊叹的 Rust 序列化库,也是我多年来使用它感到高效的一个重要原因。然而,在 Sentry 期间,我已经对它的一些限制感到非常沮丧,但实际上取代 Serde 是很棘手的,因为它在生态系统中具有强大的威力。另外,因为如果不做出一些可能痛苦的妥协,就很难真正做得更好。以下是 Serde 极端情况的三个示例,显示了 Serde 功能交互不良或意外限制: 作为映射的数字 内部标记的枚举,打开了 serde_json 的任意精度功能: #[derive(Deserialize)] #[serde(tag = "type" )] enum Shape { Circle { radius : f64 }, } serde_json :: from_str :: < Shape > ( r#"{"type": "Circle", "radius": 1.5}"# ) // 错误:无效类型:map,预期为 f64 Serde 的数据模型没有容纳任意精度数字的位置,因此 serde_json 使用带有魔术键的映射的带内信令。枚举必须缓冲字段直到它看到标签,并且缓冲区不知道魔术键。由于 Cargo 功能是统一的,因此依赖图中的任何 crate 都足以打开该功能。扁平化破坏整数键 #[derive(Deserialize)] struct Stats { Scores : HashMap < u32 , u32 > , } #[derive(Deserialize)] struct Report { name : String , #[serde(flatten)] stats : Stats , } serde_json :: from_str :: < Report > ( r#"{"name": "x", "scores": {"42": 23}}"# ) // 错误:无效类型:字符串 "42",预期 u32 位于第 1 行第 35 列 统计数据本身解析 {"scores": {"42": 23}} 就可以了。 JSON 键始终是字符串,而 serde_json 仅在类型需要时将它们转换为整数。然而,一旦拼合缓冲该值,“42”就只是一个字符串。该错误还指向文档的末尾而不是密钥。适配器不组成 fn from_hex <' de , D : Deserializer <' de >> ( d : D ) -> Result < u32 , D :: Error > { .. . } #[derive(Deserialize)] struct Theme { #[serde(deserialize_with = "from_hex" )] Primary : u32 , #[serde(deserialize_with = "from_hex" )]accent : Option < u32 > , } //error[E0308]: `?` 运算符具有不兼容的类型 // | // | #[serde(deserialize_with = "from_hex")] // | ^^^^^^^^^^ 期望 `Option`,找到 `u32` // | //help: 尝试将表达式包装在 `Some` 中 // | // | #[serde(deserialize_with = Some("from_hex"))] // | +++++ + 函数不能作为类型参数传递,因此无法将 from_hex 应用于 Option、Vec 或映射的内部。您为每个包装器编写另一个函数,一旦有了 from_opt_hex 该字段就不再是可选的,除非您还记得添加 #[serde(default)] 。这些错误都不是 Serde 中容易修复的错误。它们脱离了其设计,而该设计受到 Serde 稳定性保证的保护。早在 2022 年,我就开始了一项名为 Deser 的实验。它是 Rust 的序列化库,它采用了 Serde 的用户体验,并将其置于受 miniserde 启发的完全不同的架构之上。我从未真正完成它,它放置了几年。我把它重新捡起来,现在它已经到了我认为值得一看的地步。甚至只是为了激励其他人看看他们是否想探索这个空间。名称和想法 这个名称是 Serde,它的两半交换了。 Deser 是 Serde,但相反。在 Serde 中,类型驱动反序列化过程:Deserialize impl 向反序列化器询问它期望的值类型,格式回调访问者。每个嵌套值都通过递归处理,这使得 Serde 反序列化本质上会随着每个嵌套级别而增加堆栈。另一方面,Deser 扭转了这一局面,格式告诉下一个值的类型并将事件推送到接收器中。当接收器到达嵌套值的开头时,它不会调用它,而是将新接收器交还给驱动程序,这将所有状态保留在堆上(实际上,在竞技场中)。在退出时,发射器返回其嵌套值,而不是递归到其中。这也意味着 Deser 无法支持像 protobuf 这样非自我描述的格式。事实上,它们是故意完全排除在设计之外的。这是一种说法:如果你想“修复”Serde,你需要做出一些其他的妥协。 Deser 的想法的大部分原因可以追溯到 Sentry Relay,它处理大量不受信任的 JSON。多年来,当我在 Sentry 工作时,我们一次又一次地遇到同样的问题,其中许多问题并不是 Serde 中的真正错误,而是其设计的后果。 Serde 的稳定性保证意味着,如果不破坏每种格式和每个手写实现,就无法修复其中的许多问题。这些问题大部分来自三个决定: 适用于所有格式的一组特征。 Serde 提供自描述格式(JSON、YAML、TOML 等)以及读者必须预先了解类型的格式
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱