bincode-org / bincode

A binary encoder / decoder implementation in Rust.
MIT License
2.65k stars 265 forks source link

Use enum for versioned message dispatch #656

Closed jonatanzeidler closed 1 year ago

jonatanzeidler commented 1 year ago

Hi there. I am considering using an enum with different message types to be serialized with bincode. The enum definition might be extended over time (nothing removed). The way I read the specification, I should be able to encode/decode instances from different versions (forward and backward), as long as I make sure that new versions only add variants to the end of the list (and never remove any). On decoding part I'll get a Err(Custom(...)) if the variant is not known to the receiver.

Am I correct to assume this approach is safe and stable or am I overlooking something?

Here an example:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
enum OldMsg {
    Msg1(u32),
}

#[derive(Serialize, Deserialize, Debug)]
enum NewMsg {
    Msg1(u32),
    Msg2(u64),
}

fn main() {
    println!(
        "Old message Msg1 to new: {:?}",
        bincode::deserialize::<NewMsg>(
            &bincode::serialize(&OldMsg::Msg1(2)).unwrap()
        ).unwrap()
    );
    println!(
        "New message Msg1 to old: {:?}",
        bincode::deserialize::<OldMsg>(
            &bincode::serialize(&NewMsg::Msg1(2)).unwrap()
        ).unwrap()
    );
    // expecting an `Err(Custom())` here
    println!(
        "New message Msg2 to old: {:?}",
        bincode::deserialize::<OldMsg>(
            &bincode::serialize(&NewMsg::Msg2(2)).unwrap()
        )
    );
}

Output:

Old message Msg1 to new: Msg1(2)
New message Msg1 to old: Msg1(2)
New message Msg2 to old: Err(Custom("invalid value: integer `1`, expected variant index 0 <= i < 1"))
VictorKoenders commented 1 year ago

Yes, this is the intended way to deal with compatibility

jonatanzeidler commented 1 year ago

Thanks for the confirmation!