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_attributeadds attributes to specified fields.type_attributeadds attributes to specified messages, enums, and oneofs.message_attributeadds attributes to specified messages.enum_attributeadds 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 theDebugtrait 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 theNametrait for message types to support encoding asAnytypes.type_name_domain: Specify the domain name prefix for message type URLs.prost_path: Set the path for deriving theMessagetrait.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
BTreeMapfor all map fields - Generate
bytes::Bytesfor thedatafield ofMyMessage - Derive
SerializeandDeserializetraits forMyMessage - Customize the serialized field name for
my_fieldtomyField - Generate
Boxtype for thelarge_datafield - Skip generating
Debugimplementation 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.
