我试图解决序列化和反序列化Box<SomeTrait>
的问题。我知道在封闭类型层次结构的情况下,推荐的方法是使用枚举,并且序列化没有问题,但在我的情况下使用枚举是不合适的解决方案。
起初我尝试使用Serde,因为它是事实上的Rust序列化机制。 Serde能够序列化Box<X>
,但在X
是特征的情况下不能。无法为特征对象实现Serialize
特征,因为它具有泛型方法。使用erased-serde可以解决此特定问题,因此Box<SomeTrait>
的序列化可以正常工作。
主要问题是反序列化。要反序列化多态类型,您需要在序列化数据中使用某种类型标记。此标记应首先反序列化,之后用于动态获取将返回Box<SomeTrait>
的函数。
std::any::TypeId
可以用作标记类型,但主要问题是如何动态获取反序列化函数。我不考虑为应用程序初始化期间应手动调用的每种多态类型注册函数的选项。
我知道有两种可能的方法:
但Rust中没有这些选项。如果有的话,如何在Rust中添加多态对象的反序列化?
答案 0 :(得分:3)
README
中解释了这个概念,很聪明。
它如何工作?
我们使用
inventory
板条箱来生成具有您特质的impls的注册表,该注册表基于ctor
板条箱来连接插入注册表中的初始化函数。第一个Box<dyn Trait>
反序列化将执行迭代注册表并为反序列化功能建立标签映射的工作。随后的反序列化在该图中找到正确的反序列化功能。还涉及erased-serde
条板箱,以不破坏对象安全的方式完成所有操作。
总而言之,声明为[de] serializable的trait的每个实现都在编译时进行注册,如果对trait对象进行[反]序列化,则可以在运行时解决。
答案 1 :(得分:0)
您的所有图书馆都可以提供由std::sync::Once
保护的注册例程,该注册例程将一些标识符注册到公共static mut
,但显然您的程序必须全部调用它们。
我不知道TypeId
是否在具有不同依赖关系的重新编译中产生一致的值。
答案 2 :(得分:0)
应该可以实现这样做的库。要创建这样的库,我们将在使用库之前创建从TypeId到类型名称的双向映射,然后使用它来对类型标记进行序列化/反序列化。可以使用一个函数来注册不属于您的包的类型,并提供一个宏注释,自动为包中声明的类型执行此操作。
如果有一种方法可以访问宏中的类型ID,那么在编译时而不是运行时检测TypeId和类型名称之间的映射是一种很好的方法。