一个受 Python 启发的 Rust API,由 Serde 提供动力

多年前,作者在 Rust 中重新实现 Python 代码,需将 Python 的动态能力(__getattr__)适配到 Rust 的严格编译世界。借助serde序列化箱,作者开始尝试。文中以获取系统中安装的物理风扇信息为例,展示 Python 擅长用清晰、简洁且直观的接口抽象复杂实现,而 Rust 则不同。

抽象对象

用纯 Rust 表达“原始”API,有ValueObject两种类型,Object可获取命名属性(Value实例),但用户使用较痛苦,需手动检查Value枚举变体。

设计思路

受 Pythonic API 启发,可让用户定义查询对象的自定义结构体,指定query返回该结构体实例,类似let res: Vec<Fan> = api::query();。但如何实现此功能需考虑使用的底层 API 和泛型返回类型等。

逐步改进

先定义新特质Queryable,包含获取对象名和从Object构建实例的方法,让query函数能返回特定类型实例,用户只需为新类型实现该特质。但此方法繁琐易错,可借助serde,它能生成Deserialize实现,让用户只需添加derive(Deserialize)即可。

Serde 救援

Serde 是 Rust 中序列化和反序列化数据的框架,通过derive可生成Deserialize实现。以Fan结构体为例,使用serde_json进行反序列化,只需添加#[derive(Debug, Deserialize)]和相关配置。利用 Serde 可假装为另一种数据格式,创建接受实现Deserialize的结构体的query函数,方便用户。

深入探究

要使用Deserialize trait,需了解其内部原理。手动实现Deserialize时,需定义新的实用结构体和实现相关特质,如FanVisitor,它通过visit_map处理从反序列化器获取的数据,确定如何构建Fan结构体。

实现反序列化器

使用Deserialize的原因是其可由 Serde 为用户类型derive,更新query函数以使用DeserializeOwned,引入新的中间结构体ObjectDeserializer和特质T::deserialize(ObjectDeserializer { obj }),以解耦反序列化器和用户的T。实现ObjectDeserializerdeserialize_struct方法时,需定义实现serde::de::MapAccess的新结构体ObjectMapAccess,并在其中实现next_key_seednext_value_seed函数,处理获取字段名和值的逻辑。

总结

最终流程为用户调用query函数传入实现DeserializeTquery获取对象并调用T::Deserialize,通过一系列步骤构建T的实例。文中还提到可探索的其他替代方案和未来工作,如嵌套对象支持、新类型支持等,并展示了如何获取T的名称等。总之,通过 Rust 的 trait 系统和 Serde 的特性,可构建有趣且便捷的 API。

阅读 11
0 条评论