Generating Code with tonic-build
Compile proto into Rust.
Generating Code with tonic-build is a free Learn Rust Coding lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What tonic-build Does
tonic-build runs at compile time and turns your .proto files into Rust source. It wraps prost-build for messages and adds the gRPC service traits and clients on top.
It runs from a Cargo build script (build.rs), so generation happens automatically before your crate compiles.
Cargo Dependencies
You need runtime crates and a build-time crate. tonic and prost are normal dependencies; tonic-build goes under [build-dependencies].
tonic relies on tokio as its async runtime, so include it too.
[dependencies]
tonic = "0.12"
prost = "0.13"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
tonic-build = "0.12"A Minimal build.rs
Create build.rs at the crate root. Call tonic_build::compile_protos with the path to your proto file.
This compiles both client and server code by default and writes it to the Cargo OUT_DIR.
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/greeter.proto")?;
Ok(())
}Configuring the Builder
For more control use tonic_build::configure(). You can disable client or server generation, set output paths, or add type attributes.
Here we generate the server but skip the client, useful for a pure backend crate.
tonic_build::configure()
.build_client(false)
.build_server(true)
.compile_protos(&["proto/greeter.proto"], &["proto"])?;Include Paths
The second argument to compile_protos is the list of include directories. Imports inside your proto, like google/protobuf/empty.proto, are resolved against these roots.
Always include the directory that contains your proto files so cross-file imports resolve.
tonic_build::configure()
.compile_protos(
&["proto/greeter.proto", "proto/health.proto"],
&["proto"],
)?;Where the Code Lands
Generated files are written to the directory in the OUT_DIR environment variable, named after the proto package, for example greeter.v1.rs.
You bring it into your crate with the include_proto! macro inside a module.
pub mod greeter {
pub mod v1 {
tonic::include_proto!("greeter.v1");
}
}What Gets Generated
For each service tonic produces a server module with a trait (for example greeter_server::Greeter) and a GreeterServer wrapper, plus a client struct GreeterClient.
Each message becomes a Rust struct deriving Clone, PartialEq, and prost's Message.
// generated (sketch):
// pub mod greeter_server { pub trait Greeter { /* methods */ } }
// pub mod greeter_client { pub struct GreeterClient<T> { /* ... */ } }Adding Derives with type_attribute
You often want extra derives on generated structs, such as serde::Serialize. Use type_attribute to inject attributes onto specific types or all of them with ..
This lets generated messages flow into JSON APIs or test fixtures.
tonic_build::configure()
.type_attribute(".", "#[derive(serde::Serialize)]")
.compile_protos(&["proto/greeter.proto"], &["proto"])?;Triggering Rebuilds
Cargo reruns build.rs only when it thinks inputs changed. Emit cargo:rerun-if-changed lines so edits to proto files force regeneration.
Without this, a changed proto may not regenerate until you touch a Rust file.
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=proto/greeter.proto");
tonic_build::compile_protos("proto/greeter.proto")?;
Ok(())
}protoc Requirement
Historically tonic-build shelled out to the protoc compiler, which had to be installed. Modern versions can use the pure-Rust protox parser to avoid that dependency.
If you see a missing-protoc error, install protoc or enable a vendored compiler feature.
// In CI you may install protoc, e.g.:
// apt-get install -y protobuf-compilerFile Descriptor Sets
For reflection or advanced tooling, ask tonic-build to emit a file descriptor set with file_descriptor_set_path.
The resulting bytes can feed tonic-reflection, letting tools like grpcurl discover your services at runtime.
tonic_build::configure()
.file_descriptor_set_path(
std::env::var("OUT_DIR").unwrap() + "/greeter.bin")
.compile_protos(&["proto/greeter.proto"], &["proto"])?;Quick Check
Where does generated tonic code go and how is it loaded?
Recap
You set up dependencies, wrote a build.rs, configured client/server generation and include paths, loaded code via include_proto!, added derives, handled rebuild triggers, and learned about protoc and descriptor sets.
Next you will implement the server trait that tonic generated.
Frequently asked questions
Is the “Generating Code with tonic-build” lesson free?
Yes — the full text of “Generating Code with tonic-build” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.
What will I learn in “Generating Code with tonic-build”?
Compile proto into Rust. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Learn Rust Coding?
No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Generating Code with tonic-build” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Learn Rust Coding lesson?
Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Protobuf and Service Definitions
- Generating Code with tonic-build
- Implementing a gRPC Server
- Calling from a gRPC Client