The ‘prost‘ crate is a Rust implementation of the Protocol Buffers serialization and deserialization tool. Here’s a practical example of how to use the ‘prost‘ crate in Rust for serialization and deserialization of Protocol Buffers messages:
To start, we need to define our Protocol Buffers schema in a ‘.proto‘ file. Here’s a sample schema:
syntax = "proto3";
package messages;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
repeated PhoneNumber phones = 4;
}
message PhoneNumber {
string number = 1;
int32 type = 2;
}
We then use the ‘prost_build‘ crate to generate Rust code from our schema. In ‘Cargo.toml‘, we include the following:
[dependencies]
prost = "0.7.0"
prost-build = "0.7.0"
Our ‘build.rs‘ looks like this:
fn main() {
// Generate Rust code from the .proto file(s):
tonic_build::configure()
.build_server(false)
.compile(
&["path/to/messages.proto"],
&[],
).unwrap();
// The `configure()` function above is equivalent to:
protoc_rust::Codegen::new()
.out_dir("src/protos")
.inputs(&["path/to/messages.proto"])
.include("path/to/")
.run()
.expect("Could not compile protos!");
// Generate rustdoc links for the generated code:
println!("cargo:rustc-env=PROTOC=protoc");
println!("cargo:rerun-if-changed=path/to/messages.proto");
}
In the ‘src‘ directory, we have a ‘lib.rs‘ file that defines our Rust structs that correspond to the Protocol Buffers messages:
use prost::Message;
#[derive(Clone, PartialEq, Message)]
pub struct PhoneNumber {
#[prost(string, tag="1")]
pub number: String,
#[prost(int32, tag="2")]
pub phone_type: i32,
}
#[derive(Clone, PartialEq, Message)]
pub struct Person {
#[prost(string, tag="1")]
pub name: String,
#[prost(int32, tag="2")]
pub id: i32,
#[prost(string, tag="3")]
pub email: String,
#[prost(message, tag="4")]
pub phones: Vec<PhoneNumber>,
}
We can now use our generated Rust code to serialize and deserialize Protocol Buffers messages. Here’s an example of how to serialize a ‘Person‘ message:
fn serialize_person() -> Result<Vec<u8>, Box<dyn Error>> {
let person = Person {
name: "Alice".to_string(),
id: 1234,
email: "alice@example.com".to_string(),
phones: vec![
PhoneNumber {
number: "555-1212".to_string(),
phone_type: 1,
},
],
};
let mut buf = Vec::new();
person.encode(&mut buf)?;
Ok(buf)
}
And here’s an example of how to deserialize a ‘Person‘ message:
fn deserialize_person(buf: &[u8]) -> Result<Person, Box<dyn Error>> {
let person = Person::decode(buf)?;
Ok(person)
}
In a real-world application, you may receive serialized Protocol Buffers messages over the network or in a file, and you would use the ‘decode‘ function to parse the message into a Rust struct. You may also use the ‘encode‘ function to serialize Rust structs as Protocol Buffers messages to send over the network or write to a file.