Rust Prost-Build Usage Guide
prost is a popular Protobuf implementation and code generation tool for the Rust language. prost_build is the build-time code generation library of prost, which can generate Rust data structures and trait implementations from .proto files. This article will provide a detailed guide on how to use prost-build.
Basic Usage
prost-build is a build dependency package used to compile .proto files in the build script (build.rs) of a Rust project. The most basic usage is to use the prost_build::compile_protos
function:
fn main() -> Result<(), Box<dyn std::error::Error>> {
prost_build::compile_protos(
&["path/to/foo.proto", "path/to/bar.proto"],
&["path/to/include"]
)?;
Ok(())
}
This will compile the specified .proto files and generate the corresponding Rust code. The first argument is a list of paths to .proto files, and the second argument is a list of directories containing .proto files, used to resolve imported .proto files.
The generated Rust code will be written to the OUT_DIR
directory specified by Cargo. You can include the generated modules in your Rust code using the include!
macro.
Configuration Options
prost-build provides many configuration options to customize the generated code. These options are set through the prost_build::Config
struct.
btree_map
The btree_map
method instructs prost-build to generate BTreeMap
types for specified fields, instead of the default HashMap
. This is useful in no-std environments.
let mut config = prost_build::Config::new();
config.btree_map(&["."]); // Use BTreeMap for all map fields
config.btree_map(&[".my_package.MyMessage.my_field"]); // Use for specified field only
bytes
The bytes
method instructs prost-build to generate bytes::Bytes
types for specified byte fields, instead of the default Vec<u8>
.
config.bytes(&["."]); // Use Bytes for all bytes fields
Attributes
prost-build allows you to add custom attributes to the generated types and fields. This is useful for implementing trait derivations or integrating with other crates like serde.
field_attribute
adds attributes to specified fields.type_attribute
adds attributes to specified messages, enums, and oneofs.message_attribute
adds attributes to specified messages.enum_attribute
adds attributes to specified enums and oneofs.
config.field_attribute(".my_package.MyMessage.id", "#[serde(rename = \"id\")]");
config.type_attribute(".my_package.MyMessage", "#[derive(Serialize, Deserialize)]");
boxed
The boxed
method instructs prost-build to generate Box<T>
types for specified fields.
config.boxed(".my_package.MyMessage.large_field");
Service Generators
You can customize the way prost-build generates gRPC server and client code using the service_generator
method.
config.service_generator(Box::new(MyServiceGenerator));
Other Options
prost-build provides several other options:
compile_well_known_types
: Generate Protobuf’s well-known types from .proto files instead of using the prost_types crate.disable_comments
: Disable generating documentation comments.skip_debug
: Skip implementing theDebug
trait for specified types.extern_path
: Declare externally provided Protobuf packages or types for referencing prost types from other crates.file_descriptor_set_path
: Write the FileDescriptorSet to the specified path, which can be used for implementing reflection and other features.skip_protoc_run
: In combination withfile_descriptor_set_path
, generate code from the provided FileDescriptorSet instead of invoking protoc.retain_enum_prefix
: Prevent prost from stripping the enum name prefix from enum variant names.out_dir
: Specify the output directory for the generated Rust files.default_package_filename
: Set the filename for Protobuf without package definitions.enable_type_names
: Implement theName
trait for message types to support encoding asAny
types.type_name_domain
: Specify the domain name prefix for message type URLs.prost_path
: Set the path for deriving theMessage
trait.protoc_arg
: Add command-line arguments for protoc.include_file
: Generate a module file that includes all generated files for simplified inclusion.format
: Control whether to format the generated code using prettyplease.
Example
Here is a complete build.rs
example showcasing multiple configuration options in prost-build:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config = prost_build::Config::new();
// Generate BTreeMap for all map fields
config.btree_map(&["."]);
// Generate bytes::Bytes for bytes field
config.bytes(&[".my_package.MyMessage.data"]);
// Add derived Serialize/Deserialize
config.type_attribute(".my_package.MyMessage", "#[derive(Serialize, Deserialize)]");
// Customize serialized field name
config.field_attribute(".my_package.MyMessage.my_field", "#[serde(rename = \"myField\")]");
// Generate Box for large field
config.boxed(".my_package.MyMessage.large_data");
// Skip generating debug implementation
config.skip_debug(&["."]);
config.compile_protos(&["proto/my_package.proto"], &["proto"])?;
Ok(())
}
In this example, we:
- Generate
BTreeMap
for all map fields - Generate
bytes::Bytes
for thedata
field ofMyMessage
- Derive
Serialize
andDeserialize
traits forMyMessage
- Customize the serialized field name for
my_field
tomyField
- Generate
Box
type for thelarge_data
field - Skip generating
Debug
implementation for all types - Compile
proto/my_package.proto
, with include directoryproto
By configuring these options, we can fully customize the Protobuf code generation results.
Summary
prost-build provides powerful and flexible capabilities for integrating Protobuf with Rust. By understanding its configuration options in depth, we can generate customized Rust code tailored to our project’s needs. Whether it’s simple message types or complex gRPC services, prost-build can meet your requirements.