4个版本
0.2.1 | 2021年7月13日 |
---|---|
0.2.0 | 2021年7月5日 |
0.1.2 | 2021年6月29日 |
0.1.0 | 2021年6月26日 |
#1135 in 数据结构
35 每月下载量
在 2 crates 中使用
33KB
419 行
aversion
Rust中的版本化数据结构
此crate仍在开发中。
此crate的目标是使版本化数据结构变得简单。例如,假设我们从以下结构体开始
struct FooV1 {
val: u32,
}
如果我们以这种格式将数据序列化到文件中,但后来发现我们想要进行更改
struct FooV2 {
val: u64,
}
...那么如果我们想要支持我们之前的文件,我们就有很多工作要做。我们可能需要在文件头中增加一个版本号,并且可能需要通过所有使用 FooV1
的不同位置,并决定是否将其升级到 FooV2
。任何使用 FooV1
的地方,我们都必须保留该代码,否则可能会破坏兼容性。
此crate添加了允许我们跟踪每个结构体的版本的特质,并派生出特质和方法,允许任何结构体动态升级到最新版本。这意味着大多数代码只需与最新版本交互,同时仍然能够读取旧文件。
为了使这成为可能,结构体必须遵循特定的模式
- 版本化结构体必须遵循命名约定
Name
+V
+{integer}
,即FooV1
或BarV42
。 - 版本必须从1开始,并且是连续的。
- 必须有一个类型别名
type Foo = FooV3
指向最新版本。 - 对于每一对版本,
N
和N+1
,必须实现特质FromVersion
。例如
impl FromVersion<FooV1> for FooV2 {
fn from_version(v1: FooV1) -> Self {
FooV2 { val: v1.val.into() }
}
}
此crate仍较新,这些规则可能在将来发生变化。
反序列化
我们希望能够在不知道存储版本的情况下反序列化数据结构。
为此,我们使用 DataSource
特性,以指定我们的序列化格式、头部格式和错误类型。
一旦实现了 UpgradeLatest
特性(为此有一个 derive 宏),我们就可以快速反序列化任何版本的数据结构,例如:
// Define a data source
let src = CborData::new(...);
// Read a the next header + message, and upgrade to the latest version
let msg: Foo = data_src.expect_message()?;
请注意,在这个例子中,msg
总是 Foo
结构体的最新版本,无论实际存储的是哪个版本。只要 FromVersion
代码正确,程序的其他部分就无需知道从文件中读取的是哪个版本。
消息组
我们可以将此逻辑扩展到不同消息的组,以自动构建一个分发函数。例如,如果我们定义了一组消息:
enum MyProtocol {
Foo(Foo),
Bar(Bar),
}
我们可以派生 GroupDeserialize
特性,它可以自动反序列化 MyProtocol
中的任何消息
let incoming_message = MyProtocol::read_message(&mut my_data_source)?;
match incoming_message {
Foo(f) => {
// handle the received Foo message
}
Bar(b) => {
// handle the received Bar message
}
}
与上一个例子类似,头部会告诉我们发送了哪个消息(即 Foo
或 Bar
),以及该结构的版本(FooV1
或 FooV2
),而 read_message
会反序列化结构的正确版本,将其升级到最新版本,并以 MyProtocol
枚举的形式返回,供调用者处理。
许可:Apache-2.0
依赖项
~0.9–1.5MB
~34K SLoC