Search code examples
rustbincode

How to avoid breaking serialized data where enum variants are going to be modified in the future?


Serializing with Bincode, data contains an enum like this:

enum E {
   A = 0,
   B = 1,
   C = 2,
}

The variant E::A was never used, so I thought removing it wouldn't cause a problem:

enum E {
   B = 1,
   C = 2,
}

Apparently the serializer does not care about explicit discriminants:

// deser error:
"invalid value: integer `2`, expected variant index 0 <= i < 2"

Is there a way to make "Bincode" use the explicit discriminants? Or is there a more proper solution?

Thanks.


Solution

  • The serde derive macros use the variant's index or name (chosen by the format) when serializing an enum. Bincode chooses the index.

    As long as your enum is composed entirely of unit variants, you can use serde_repr with a u32 representation.

    use serde_repr::{Deserialize_repr, Serialize_repr};
    
    #[derive(Deserialize_repr, Serialize_repr)]
    #[repr(u32)]
    enum E1 {
        A = 0,
        B = 1,
        C = 2,
    }
    
    #[derive(Deserialize_repr, Serialize_repr)]
    #[repr(u32)]
    enum E2 {
        B = 1,
        C = 2,
    }
    

    These will also be compatible in bincode specifically with an enum with serde derives:

    use serde::{Deserialize, Serialize};
    
    #[derive(Deserialize, Serialize)]
    enum E1 {
        A,
        B,
        C,
    }
    

    Note that this overrides the variant names used in human-readable formats. If you are also serializing to a human-readable format and want to preserve the variant names, or if you have variants with data, you will need to write custom Deserialize and Serialize implementations.

    Related question that I just answered: How to Serialize Enum in Rust with Bincode While Retaining Enum Discriminant Instead of Index? This one ended up being completely different because of the u64 tags, but it has lots of information on which libraries are responsible for each part of enum serialization.