Binary encoders
Recipe | Crates | Categories |
---|---|---|
bincode | ||
CBOR with ciborium | ||
prost | ||
protobuf | ||
MessagePack with rmp-serde |
bincode
bincode
is a binary serialization / deserialization strategy for transforming structs into bytes and vice versa.
// use std::fs::File; // use std::io::Cursor; // use bincode::deserialize_from; // use bincode::serialize_into; // #[derive(Serialize, Deserialize, Debug)] // struct MyData { // name: String, // age: u32, // is_active: bool, // } // fn main() -> anyhow::Result<()> { // // Create a sample data object // let data = MyData { // name: "Alice".to_string(), // age: 30, // is_active: true, // }; // // Serialize to a file // let mut file = File::create("data.bin")?; // serialize_into(&mut file, &data)?; // // Deserialize from a file // let mut file = File::open("data.bin")?; // let deserialized_data: MyData = deserialize_from(&mut file)?; // // Serialize to a buffer // let mut buf = Vec::new(); // serialize_into(&mut buf, &data)?; // // Deserialize from a buffer // let mut cursor = Cursor::new(&buf[..]); // let deserialized_data_from_buf: MyData = deserialize_from(&mut cursor)?; // // Print the deserialized data // println!("{:?}", deserialized_data); // println!("{:?}", deserialized_data_from_buf); // Ok(()) // }
ProtoBuf
prost
prost
is a Protocol Buffers implementation for the Rust Language.
In you main.rs
file:
// // This will serialize and then deserialize a Person protobuf message and // // print out its contents. // mod person { // include!(concat!(env!("OUT_DIR"), "/tests/proto/person.rs")); // } // use person::Person; // use prost::Message; // fn main() { // // Create a Person message // let person = Person { // name: "Alice".to_string(), // age: 30, // }; // // Serialize the message // let mut buf = Vec::new(); // person.encode(&mut buf).unwrap(); // // Deserialize the message // let decoded_person = Person::decode(&*buf).unwrap(); // println!("Name: {}, Age: {}", decoded_person.name, decoded_person.age); // }
In person.proto
:
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 age = 2;
}
In build.rs
:
fn prost() { prost_build::compile_protos(&["tests/proto/person.proto"], &["src/"]) .unwrap(); }
protobuf
protobuf
is a Rust implementation of Google protocol buffers.
// // In your `Cargo.toml`, add the `protobuf` crate as a dependency: // // [dependencies] // // protobuf = "3.7.1" // // Compile the .proto file to generate the Rust code. // // You can use the protoc compiler with the Rust plugin // // ```sh // // protoc --rust_out=. person.proto // // ``` // // This will generate a Rust module with the compiled code for the Person // // message. // // Import the generated code for the Person message // // The generated code is in the person.rs file // mod person; // use person::Person; // use protobuf::Message; // fn main() { // // Create an instance of the Person message // let mut person = Person::new(); // person.set_name(String::from("Alice")); // person.set_age(30); // // Serialize the Person message to a byte array // let serialized_person = person.write_to_bytes().unwrap(); // // Print the serialized data // println!("Serialized data: {:?}", serialized_person); // // Deserialize the byte array back into a Person message // let deserialized_person = // Person::parse_from_bytes(&serialized_person).unwrap(); // // Print the deserialized Person message // println!("Deserialized person: {:?}", deserialized_person); // }
MessagePack with rmp-serde
rmp-serde
connects the MessagePack
library with serde
, providing the ability to easily serialize and deserialize Rust built-in types, types from the standard library, and custom data structures.
// use std::io::Cursor; // // use std::io::Read; // // use std::io::Write; // use rmp_serde::Deserializer; // use rmp_serde::Serializer; // use serde::Deserialize; // use serde::Serialize; // // Provides serialization and deserialization to/from MessagePack. // // Key features of rmp_serde: // // Efficient: MessagePack is a compact binary format, leading to smaller // // serialized data sizes and faster serialization/deserialization. Versatile: // // Supports a wide range of data types and can be used with various // input/output // sources. Interoperable: MessagePack is language-agnostic, // enabling data // exchange between different programming languages. // #[derive(Serialize, Deserialize, Debug)] // struct MyData { // name: String, // age: u32, // is_active: bool, // } // fn main() -> Result<(), Box<dyn std::error::Error>> { // // Create a sample data object // let data = MyData { // name: "Alice".to_string(), // age: 30, // is_active: true, // }; // // Serialize to MessagePack // let mut buf = Vec::new(); // // Creates a serializer that writes to the buffer, // // Serializes the data object into MessagePack format and writes it to // the // buffer. // data.serialize(&mut Serializer::new(&mut buf))?; // // Deserialize from MessagePack // let mut cursor = Cursor::new(&buf[..]); // let deserialized_data: MyData = // Deserializer::from_reader(&mut cursor)?.deserialize()?; // // Print the deserialized data // println!("{:?}", deserialized_data); // Ok(()) // }
CBOR with ciborium
Concise Binary Object Representation is a binary data serialization format loosely based on JSON. ciborium
is a serde
implementation of CBOR using ciborium-basic.
use std::io::Cursor; // use std::io::Read; // use std::io::Write; use ciborium::de::from_reader; use ciborium::ser::into_writer; use serde::Deserialize; use serde::Serialize; #[derive(Serialize, Deserialize, Debug)] struct MyData { name: String, age: u32, is_active: bool, } fn main() -> anyhow::Result<()> { // Create a sample data object let data = MyData { name: "Alice".to_string(), age: 30, is_active: true, }; // Serialize to CBOR let mut buf = Vec::new(); into_writer(&data, &mut buf)?; // Deserialize from CBOR let mut cursor = Cursor::new(&buf[..]); let deserialized_data: MyData = from_reader(&mut cursor)?; // Print the deserialized data println!("{:?}", deserialized_data); Ok(()) }
Flatbuffers
flatbuffers
is the official FlatBuffers Rust runtime library. FlatBuffers is a free software library implementing a serialization format similar to Protocol Buffers, Thrift, Apache Avro, SBE, and Cap'n Proto, open-sourced by Google. It supports "zero-copy" deserialization, so that accessing the serialized data does not require first copying it into a separate part of memory.
// use flatbuffers::FlatBufferBuilder; // // The `flatbuffers` crate provides runtime support // // for the FlatBuffers format. // // 'FlatBuffers' is a cross platform serialization library // // architected for maximum memory efficiency. // // It allows you to directly access serialized data // // without parsing/unpacking it first, while // // still having great forwards/backwards compatibility. // // It was originally created at Google for game development // // and other performance-critical applications. // // Make sure to run flatc to generate the Rust code from the `.fbs` file: // // flatc --rust -o . my_game.fbs // // https://flatbuffers.dev/ // // Define the schema for our data // flatbuffers_gen::r#gen::include_file!("my_game.fbs"); // Assuming your schema // is in my_game.fbs // fn main() { // // Create a FlatBufferBuilder // let mut builder = FlatBufferBuilder::new(); // // Create some data to store // let name = builder.create_string("MyMonster"); // let hp = 100; // // Create an object of our Monster struct // let monster = Monster::create( // &mut builder, // &MonsterArgs { // name: Some(name), // hp, // ..Default::default() // } // ); // // Finish building the buffer // builder.finish(monster, None); // // Get the finished buffer as a byte slice // let finished_bytes = builder.finished_data(); // // Now you can: // // 1. Serialize the finished_bytes to a file or send it over the network // // 2. Deserialize the finished_bytes to access the data in another // program // println!("FlatBuffer size: {}", finished_bytes.len()); // }
Cap'n Proto
capnp
is the runtime library for Cap'n Proto data encoding. capnpc
is used for Cap'n Proto code generation.
// use capnp::serialize; // use foo_capnp::my_struct::Builder; // // This crate contains basic facilities for reading and writing Cap'n Proto // messages in Rust. It is intended to be used in conjunction with code // generated by the capnpc-rust crate. // // Add the following dependencies to your `Cargo.toml`: // // [dependencies] // // capnp = "0.20.3" # or latest version // // [build-dependencies] // // capnpc = "0.20.1 // // Make sure the following lines are in your `build.rs` file: // // ```rust // // fn main() { // // capnpc::CompilerCommand::new() // // //.src_prefix("schema") // // .file("tests/proto/foo.capnp") // // .run() // // .expect("schema compiler command"); // // } // // ``` // // Or compile the `.capnp` file manually using the `capnp` compiler: // // ```sh // // capnp compile -o . foo.capnp // // ``` // // Define the schema for our data // mod foo_capnp { // include!(concat!(env!("OUT_DIR"), "/tests/proto/foo_capnp.rs")); // // Assuming your schema is in foo_capnp.capnp } // fn main() { // // Create a message builder // let mut message = Builder::new_default(); // // Create a root object of our MyStruct type // let mut root = message.init_root::<foo_capnp::my_struct::MyStruct>(); // // Sets the value of the name field. // root.set_name("Hello from Rust"); // // Sets the value of the value field. // root.set_value(42); // // Serialize the message // let mut write_buffer = Vec::new(); // serialize::write_message(&mut write_buffer, &message).unwrap(); // // Now you can: // // 1. Serialize the write_buffer to a file or send it over the network // // 2. Deserialize the write_buffer to access the data in another program // println!("Cap'n Proto message size: {}", write_buffer.len()); // }
In build.rs
:
fn capnp() { capnpc::CompilerCommand::new() //.src_prefix("schema") // For all files specified for compilation that start with prefix, removes the prefix when computing output filenames. .file("tests/proto/foo.capnp") // Add a file to the list of files to be compiled. .run() .expect("schema compiler command"); }
In foo.capnp
:
@0xb729500ee8baacd8;
struct MyStruct {
name @0 : Text;
value @1 : Int32;
}
Key Features of Cap'n Proto
- Compact and efficient: Cap'n Proto is designed to be very space-efficient for both on-the-wire and in-memory representations.
- Fast: Cap'n Proto offers excellent performance, especially for serialization and deserialization.
- Language-agnostic: You can generate code in various programming languages from a single .capnp definition file.
- Schema evolution: Cap'n Proto supports schema evolution, allowing you to modify the structure of your data over time without breaking compatibility.