Rust Serde Versioning

Serde is an amazing collection of Rust libraries for Serialization and Deserialization with many useful supported formats.

For example, I can convert a YAML file into a native Rust struct by just slapping a #[derive(Deserialize)] above the struct.

One challenge that I faced was reading YAML files of different schema versions.

One option is to use Option<T> for all fields which changed between versions. Generally speaking, this is probably the easiest and most suitable solution. However, when parsing is also used as a linter, this will not suffice. For example, we might have a version 1.0 that has fields a and b (both required). In version 2.0, only a remains and b is replaced by c.

To solve this, I found that tagged enums make for the best representation.

Here’s an example input file:

my_version: 1.0
a: "bla"
b: "blub"

Here’s the MyFile enum, internally tagged on the field my_version:

#[derive(Debug, Deserialize)]
#[serde(tag = "my_version")]
enum MyFile {
    #[serde(rename = "1.0")]
    V1(MyFileV1),
    #[serde(rename = "2.0")]
    V2(MyFileV2),
}

And the MyFileV1 looks like this:

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct MyFileV1 {
    a: String,
    b: String,
}

You can see a free standing example here on the Rust playground.