使用 tonic-build 生成代码
将 proto 编译为 Rust。
使用 tonic-build 生成代码 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 4 节课。
tonic-build 的作用
tonic-build 在编译时运行,并将您的 .proto 文件转换为 Rust 源代码。它会封装 prost-build 来处理消息,并在此基础上添加 gRPC 服务特征和客户端。
它通过 Cargo 构建脚本(build.rs)运行,因此会在您的 crate 编译前自动完成代码生成。
Cargo 依赖
您需要运行时 crate 和构建时 crate。tonic 与 prost 是普通依赖;tonic-build 应放在 [build-dependencies] 下。
tonic 依赖 tokio 作为异步运行时,因此也请将其加入依赖。
[dependencies]
tonic = "0.12"
prost = "0.13"
tokio = { version = "1", features = ["full"] }
[build-dependencies]
tonic-build = "0.12"最小化的 build.rs
在 crate 根目录创建 build.rs。调用 tonic_build::compile_protos,并传入 proto 文件的路径。
默认情况下,它会同时编译客户端和服务器代码,并将生成结果写入 Cargo 的 OUT_DIR。
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/greeter.proto")?;
Ok(())
}配置构建器
如需更精细的控制,请使用 tonic_build::configure()。您可以禁用客户端或服务器代码生成、设置输出路径,或添加类型属性。
这里我们生成服务器代码而跳过客户端代码,这对于纯后端 crate 很有用。
tonic_build::configure()
.build_client(false)
.build_server(true)
.compile_protos(&["proto/greeter.proto"], &["proto"])?;包含路径
compile_protos 的第二个参数是包含目录列表。proto 中的导入(例如 google/protobuf/empty.proto)会根据这些根目录解析。
请始终包含存放 proto 文件的目录,以便正确解析跨文件导入。
tonic_build::configure()
.compile_protos(
&["proto/greeter.proto", "proto/health.proto"],
&["proto"],
)?;代码生成到哪里
生成的文件会写入 OUT_DIR 环境变量指定的目录,并以 proto 包命名,例如 greeter.v1.rs。
您可以在模块中使用 include_proto! 宏将其引入 crate。
pub mod greeter {
pub mod v1 {
tonic::include_proto!("greeter.v1");
}
}会生成什么
对于每个服务,tonic 都会生成一个包含特征的服务器模块(例如 greeter_server::Greeter)和一个 GreeterServer 包装器,同时还会生成客户端结构体 GreeterClient。
每条消息都会变成一个派生了 Clone、PartialEq 和 prost 的 Message 的 Rust 结构体。
// generated (sketch):
// pub mod greeter_server { pub trait Greeter { /* methods */ } }
// pub mod greeter_client { pub struct GreeterClient<T> { /* ... */ } }使用 type_attribute 添加派生特征
您通常会希望为生成的结构体添加额外的派生特征,例如 serde::Serialize。使用 type_attribute 可以向特定类型或使用 . 向所有类型注入属性。
这样,生成的消息就可以用于 JSON API 或测试固件。
tonic_build::configure()
.type_attribute(".", "#[derive(serde::Serialize)]")
.compile_protos(&["proto/greeter.proto"], &["proto"])?;触发重新构建
Cargo 只有在认为输入发生变化时才会重新运行 build.rs。请输出 cargo:rerun-if-changed 行,这样修改 proto 文件就会强制重新生成代码。
否则,修改后的 proto 可能要等到您触碰某个 Rust 文件后才会重新生成。
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 要求
过去,tonic-build 会调用 protoc 编译器,因此必须先安装它。现代版本可以使用纯 Rust 的 protox 解析器,从而避免这一依赖。
如果您看到缺少 protoc 的错误,请安装 protoc,或启用包含编译器的功能。
// In CI you may install protoc, e.g.:
// apt-get install -y protobuf-compiler文件描述符集合
如需反射或高级工具支持,请让 tonic-build 使用 file_descriptor_set_path 生成文件描述符集合。
生成的字节数据可以提供给 tonic-reflection,让 grpcurl 等工具在运行时发现您的服务。
tonic_build::configure()
.file_descriptor_set_path(
std::env::var("OUT_DIR").unwrap() + "/greeter.bin")
.compile_protos(&["proto/greeter.proto"], &["proto"])?;快速检查
生成的 tonic 代码位于哪里,又是如何加载的?
回顾
您设置了依赖,编写了 build.rs,配置了客户端和服务器代码生成及包含路径,通过 include_proto! 加载代码,添加了派生特征,处理了重新构建触发条件,并了解了 protoc 和描述符集合。
接下来,您将实现 tonic 生成的服务器特征。
常见问题解答
「使用 tonic-build 生成代码」课时是免费的吗?
是的 — 「使用 tonic-build 生成代码」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 4 节课。
「使用 tonic-build 生成代码」这节课中我会学到什么?
将 proto 编译为 Rust。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Learn Rust Coding 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「使用 tonic-build 生成代码」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Learn Rust Coding 课中编写并运行代码吗?
能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Protobuf 与服务定义
- 使用 tonic-build 生成代码
- 实现 gRPC 服务器
- 从 gRPC 客户端调用