Is it better to use bincode or postcard?

Is it better to use serde with bincode or with postcard?

From what I wrote in another thread:

I personally would prefer Postcard currently, but I didn't work much with either yet.

4 Likes

I found another difference between Bincode and Postcard.

Bincode (as of 2.0.0-rc.2) allows configuration of whether (fixed) array lengths are included on wire, which is also the default, while Postcard never includes it for arrays (of known length).

See tuple in the Postcard wire spec (which seems to be used for fixed-size arrays, see types in serde data model). Note that slices always contain the length though.

Edit: Apparently bincode::encode_to_vec and bincode::serde::encode_to_vec are different, incompatible functions, as of release candidate 2 of bincode version 2.0 See this other thread.

// uses `postcard` version 1.0.2
// uses `bincode` version "2.0.0-rc.2"

fn main() {
    let tuple: (u8, u8) = (44, 55);
    let array: [u8; 3] = [11, 12, 13];
    let slice: &[u8] = &array;
    let stdcfg = bincode::config::standard();
    let skipcfg = bincode::config::standard().skip_fixed_array_length();

    assert_eq!(postcard::to_stdvec(&tuple).unwrap(), &[44, 55]);
    assert_eq!(postcard::to_stdvec(&array).unwrap(), &[11, 12, 13]);
    assert_eq!(postcard::to_stdvec(&slice).unwrap(), &[3, 11, 12, 13]);

    assert_eq!(bincode::encode_to_vec(&tuple, stdcfg).unwrap(), &[44, 55]);
    assert_eq!(bincode::encode_to_vec(&array, stdcfg).unwrap(), &[3, 11, 12, 13]);
    assert_eq!(bincode::encode_to_vec(&slice, stdcfg).unwrap(), &[3, 11, 12, 13]);

    assert_eq!(bincode::encode_to_vec(&tuple, skipcfg).unwrap(), &[44, 55]);
    assert_eq!(bincode::encode_to_vec(&array, skipcfg).unwrap(), &[11, 12, 13]);
    assert_eq!(bincode::encode_to_vec(&slice, skipcfg).unwrap(), &[3, 11, 12, 13]);
}

(no Playground due to postcard not being available there)

Moreover, Bincode removed the native endian encoding, it seems (see migration guide for rc.2).

Overall, Postcard is less configurable and seems more minimalistic.

Side note: Due to the different variable integer encoding, Postcard is a bit more efficient for integers in the range of 251 to 16383 (unsigned), which take 2 bytes in Postcard, but three bytes in Bincode (with default config). On the other hand, Bincode stores smaller integers (in the range of 128 to 250) better. So it's a tradeoff:

// uses `postcard` version 1.0.2
// uses `bincode` version "2.0.0-rc.2"

fn main() {
    let stdcfg = bincode::config::standard();
    assert_eq!(postcard::to_stdvec(&128u32).unwrap(), &[128, 1]);
    assert_eq!(postcard::to_stdvec(&250u32).unwrap(), &[250, 1]);
    assert_eq!(postcard::to_stdvec(&251u32).unwrap(), &[251, 1]);
    assert_eq!(bincode::encode_to_vec(&128u32, stdcfg).unwrap(), &[128]);
    assert_eq!(bincode::encode_to_vec(&250u32, stdcfg).unwrap(), &[250]);
    assert_eq!(bincode::encode_to_vec(&251u32, stdcfg).unwrap(), &[251, 251, 0]);
}

5 Likes

What would you advise me to use then for two scenarios.

Scenario #1, for a game editor that needs to store the data (such as position of objects, size of objects etc)

Scenario #2, for an actual database where I am storing and constantly retreiving data.

For each of these scenarios which crate should I use? If there is another crate that is alternative to bincode and postcard then please let me know?

I think the formats are so similar that it hardly matters. You could use either.

However, maybe implementation of the crates is performing differently well (in regard to speed), but I doubt it would be much of a difference. You could run a benchmark to find out.

I personally get confused by the variety of configuration options of bincode, so postcard seems easier to use.

Apparently bincode will (optionally) also work without serde as a dependency (at least in upcoming version 2), but I'm not sure of their plans.

Overall, I feel like postcard has a better specification/documentation.

Note, however, that both formats require that your data structure is known and doesn't change. I.e. if you later add a field to your serialized structs, you won't be able to deserialize old data. If you need such ability, then neither of those two formats are suitable.

4 Likes

You can add versions as well and detect if using old version extract extract data in a different way etc.

Yes, right, if you provide an extra mechanism for that, you can still use Postcard or Bincode.

It's also explained here:

Backwards/forwards compatibility between revisions of a postcard schema are considered outside of the scope of the postcard wire format, and must be considered by the end users, if compatible revisions to an agreed-upon schema are necessary.


I just mentioned this because of your use case, where it might be required:

I recently looked into Protocol Buffers (see protobuf.dev) and liked this approach a lot. It's very much different from postcard and bincode, and it doesn't work with serde. It does solve compatibility issues easily though, so you don't need to implement version negotiation as an extra layer. However, I feel like Rust integration is a bit difficult yet (see Protocol Buffers in Rust – what to use and how to use it?). And you might be more constrained with how your Rust data structures look like (because you usually would generate them from a .proto file). So I'm not sure if it's really better to use Protocol Buffers. It would be an entirely different approach.

Yeah I can sort of see that, you can just push stuff into structs as directly as you can with serde, so I think I would like to stick with postcard but thanks for the alternative suggestion as well :slight_smile:

I also like serde and postcard due to being easy to use.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.