如果有的话,如何在 Rust 中添加多态特征对象的反序列化?

How can deserialization of polymorphic trait objects be added in Rust if at all?

我正在尝试解决序列化和反序列化的问题Box<SomeTrait>。我知道在封闭类型层次结构的情况下,推荐的方法是使用枚举并且它们的序列化没有问题,但在我的情况下使用枚举是一个不合适的解决方案。

起初我尝试使用 Serde,因为它是事实上的 Rust 序列化机制。 Serde 能够序列化 Box<X> 但在 X 是特征的情况下不能。 Serialize trait can’t be implemented for trait objects because it has generic methods. This particular issue can be solved by using erased-serde 所以 Box<SomeTrait> 的序列化可以工作。

主要问题是反序列化。要反序列化多态类型,您需要在序列化数据中有一些类型标记。此标记应首先反序列化,然后用于动态获取将 return Box<SomeTrait>.

的函数

std::any::TypeId可以作为标记类型,但主要问题是如何动态获取反序列化函数。我不考虑为应该在应用程序初始化期间手动调用的每个多态类型注册一个函数的选项。

我知道两种可能的方法:

  1. 像C#这样有运行时反射的语言可以用它来获取 反序列化方法。
  2. 在 C++ 中,cereal 库使用静态对象的魔力在库初始化时在静态映射中注册反序列化器。

但是这些选项在 Rust 中都不可用。如果有的话,如何在 Rust 中添加多态对象的反序列化?

你所有的库都可以提供一个注册例程,由 std::sync::Once 保护,将一些标识符注册到一个公共 static mut,但显然你的程序必须调用它们。

我不知道 TypeId 是否会在具有不同依赖项的重新编译中产生一致的值。

一个库应该可以做到这一点。要创建这样一个库,我们将在使用该库之前创建一个从 TypeId 到类型名称的双向映射,然后将其用于带有类型标记的 serialization/deserialization。可以有一个函数来注册不属于您的包的类型,并提供一个宏注释,自动为您的包中声明的类型执行此操作。

如果有一种方法可以在宏中访问类型 ID,那将是在编译时而不是运行时检测 TypeId 和类型名称之间映射的好方法。

这是 implemented by dtolnay

这个概念很聪明,ans 在 README:

中有解释

How does it work?

We use the inventory crate to produce a registry of impls of your trait, which is built on the ctor crate to hook up initialization functions that insert into the registry. The first Box<dyn Trait> deserialization will perform the work of iterating the registry and building a map of tags to deserialization functions. Subsequent deserializations find the right deserialization function in that map. The erased-serde crate is also involved, to do this all in a way that does not break object safety.

总而言之,声明为 [de]serializable 的特征的每个实现都在编译时注册,并且在特征对象 [de] 序列化的情况下在运行时解决。