34次发布
0.13.1 | 2024年7月9日 |
---|---|
0.12.6 | 2024年5月17日 |
0.12.3 | 2023年11月21日 |
0.11.9 | 2023年4月12日 |
0.1.0 | 2017年6月25日 |
3 in 编码
6,118,275 downloads per month
Used in 3,877 crates (1,898 directly)
96KB
2K SLoC
PROST!
prost
is a Protocol Buffers implementation for the Rust Language. prost
generates simple, idiomatic Rust code from proto2
and proto3
files.
Compared to other Protocol Buffers implementations, prost
- Generates simple, idiomatic, and readable Rust types by taking advantage of Rust
derive
attributes. - Retains comments from
.proto
files in generated Rust code. - Allows existing Rust types (not generated from a
.proto
) to be serialized and deserialized by adding attributes. - Uses the
bytes::{Buf, BufMut}
abstractions for serialization instead ofstd::io::{Read, Write}
. - Respects the Protobuf
package
specifier when organizing generated code into Rust modules. - Preserves unknown enum values during deserialization.
- Does not include support for runtime reflection or message descriptors.
Using prost
in a Cargo Project
First, add prost
and its public dependencies to your Cargo.toml
[dependencies]
prost = "0.13"
# Only necessary if using Protobuf well-known types:
prost-types = "0.13"
The recommended way to add .proto
compilation to a Cargo project is to use the prost-build
library. See the prost-build
documentation for more details and examples.
See the snazzy repository for a simple start-to-finish example.
MSRV
prost
遵循 tokio-rs
项目的 MSRV 模型并支持 1.70。有关 tokio msrv 政策的更多信息,您可以在此查看:这里
生成的代码
prost
使用 proto2
或 proto3
语法从源 .proto
文件生成 Rust 代码。 prost
的目标是最简化生成的代码。
protoc
在 prost-build
v0.11 版本发布后,调用 compile_protos
时将需要 protoc
(除非启用了 skip_protoc
)。Prost 将不再提供捆绑的 protoc
或尝试为用户编译 protoc
。有关 protoc
的安装说明,请参阅 protobuf 安装 说明。
包
Prost 现在可以生成没有包规范的 .proto
文件的代码。prost
将 Protobuf 包转换为 Rust 模块。例如,给定 package
规范符
package foo.bar;
从文件生成的所有 Rust 类型都将位于 foo::bar
模块中。
消息
给定一个简单的消息声明
// Sample message.
message Foo {
}
prost
将生成以下 Rust 结构体
/// Sample message.
#[derive(Clone, Debug, PartialEq, Message)]
pub struct Foo {
}
字段
Protobuf 消息中的字段被转换为相应类型的公共结构体字段。
标量值
标量值类型转换为以下内容
Protobuf 类型 | Rust 类型 |
---|---|
double |
f64 |
float |
f32 |
int32 |
i32 |
int64 |
i64 |
uint32 |
u32 |
uint64 |
u64 |
sint32 |
i32 |
sint64 |
i64 |
fixed32 |
u32 |
fixed64 |
u64 |
sfixed32 |
i32 |
sfixed64 |
i64 |
bool |
bool |
string |
String |
bytes |
Vec<u8> |
枚举
所有 .proto
枚举类型转换为 Rust 的 i32
类型。此外,每个枚举类型都有一个对应的 Rust enum
类型。例如,这个 proto
枚举
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
对应到以下的 Rust 枚举 [^1]
pub enum PhoneType {
Mobile = 0,
Home = 1,
Work = 2,
}
[^1]: 为了清晰起见,省略了注释。下面是完整示例。
您可以通过以下方式将 PhoneType
值转换为 i32
PhoneType::Mobile as i32
添加到生成的 PhoneType
中的 #[derive(::prost::Enumeration)]
注释为类型添加了这些关联函数
impl PhoneType {
pub fn is_valid(value: i32) -> bool { ... }
#[deprecated]
pub fn from_i32(value: i32) -> Option<PhoneType> { ... }
}
它还添加了一个 impl TryFrom<i32> for PhoneType
实现方法,因此您可以通过以下方式将 i32
转换为其对应的 PhoneType
值,例如
let phone_type = 2i32;
match PhoneType::try_from(phone_type) {
Ok(PhoneType::Mobile) => ...,
Ok(PhoneType::Home) => ...,
Ok(PhoneType::Work) => ...,
Err(_) => ...,
}
此外,当在 Message
中将 proto
枚举用作字段时,消息将具有 '访问器' 方法来获取/设置字段的值,该值与 Rust 枚举类型相同。例如,此 proto PhoneNumber
消息包含一个名为 type
的字段,其类型为 PhoneType
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
将变成以下 Rust 类型 [^2],具有 type
和 set_type
方法
pub struct PhoneNumber {
pub number: String,
pub r#type: i32, // the `r#` is needed because `type` is a Rust keyword
}
impl PhoneNumber {
pub fn r#type(&self) -> PhoneType { ... }
pub fn set_type(&mut self, value: PhoneType) { ... }
}
请注意,如果字段具有无效的 i32
值,则获取器方法将返回 Rust 枚举的默认值。
枚举类型不直接用作字段,因为 Protobuf 规范要求枚举值是 '开放的',并且必须能够解码未识别的枚举值。
[^2]: 为了清晰起见,省略了注解。下面是完整示例。
字段修饰符
Protobuf 标量值和枚举消息字段可以根据 Protobuf 版本具有修饰符。修饰符会更改 Rust 字段对应的类型
.proto 版本 |
修饰符 | Rust 类型 |
---|---|---|
proto2 |
可选 |
Option<T> |
proto2 |
必需 |
T |
proto3 |
默认 | T 用于标量类型,否则为 Option<T> |
proto3 |
可选 |
Option<T> |
proto2 /proto3 |
重复 |
Vec<T> |
请注意,在 proto3
中,所有用户定义消息类型的默认表示形式为 Option<T>
,而对于标量类型则为 T
(在解码过程中,缺失的值由 T::default()
填充)。如果您需要一个标量类型 T
的存在证明,请使用 optional
修饰符来强制生成 Rust 结构体中的 Option<T>
表示形式。
映射字段
映射字段将转换为 Rust HashMap
,键和值类型从 Protobuf 键和值类型转换而来。
消息字段
消息字段将转换为相应的结构体类型。上面的字段修饰符表适用于消息字段,但 proto3
消息字段(默认)将包装在 Option
中。通常消息字段是无界的。如果字段类型和父类型递归嵌套,prost
会自动将消息字段装箱,以避免无限大小的结构体。
Oneof 字段
Oneof 字段转换为 Rust 枚举。Protobuf oneof
类型未命名,因此 prost
使用 oneof
字段的名称作为生成的 Rust 枚举的名称,并在结构体下定义枚举。例如,以下 proto3
消息
message Foo {
oneof widget {
int32 quux = 1;
string bar = 2;
}
}
生成以下 Rust[^3]
pub struct Foo {
pub widget: Option<foo::Widget>,
}
pub mod foo {
pub enum Widget {
Quux(i32),
Bar(String),
}
}
oneof
字段总是包装在 Option
中。
[^3]: 为了清晰起见,省略了注解。下面是完整示例。
服务
prost-build
允许使用自定义代码生成器来处理 service
定义。这可以用于根据应用程序的具体需求输出 Rust 特性。
生成代码示例
示例 .proto
文件
syntax = "proto3";
package tutorial;
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
以及生成的 Rust 代码(tutorial.rs
)
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Person {
#[prost(string, tag="1")]
pub name: ::prost::alloc::string::String,
/// Unique ID number for this person.
#[prost(int32, tag="2")]
pub id: i32,
#[prost(string, tag="3")]
pub email: ::prost::alloc::string::String,
#[prost(message, repeated, tag="4")]
pub phones: ::prost::alloc::vec::Vec<person::PhoneNumber>,
}
/// Nested message and enum types in `Person`.
pub mod person {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PhoneNumber {
#[prost(string, tag="1")]
pub number: ::prost::alloc::string::String,
#[prost(enumeration="PhoneType", tag="2")]
pub r#type: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum PhoneType {
Mobile = 0,
Home = 1,
Work = 2,
}
}
/// Our address book file is just one of these.
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AddressBook {
#[prost(message, repeated, tag="1")]
pub people: ::prost::alloc::vec::Vec<Person>,
}
访问 protoc
的 FileDescriptorSet
可以使用 prost_build::Config::file_descriptor_set_path
选项在构建和代码生成步骤中输出文件描述符集。当与 std::include_bytes
宏以及 prost_types::FileDescriptorSet
类型一起使用时,Prost 的应用程序和库可以实现需要从原始 .proto
文件详细信息的反射能力。
在 no_std
Crate 中使用 prost
prost
与 no_std
Crate 兼容。要启用 no_std
支持,请在 prost
和 prost-types
中禁用 std
功能。
[dependencies]
prost = { version = "0.13.1", default-features = false, features = ["prost-derive"] }
# Only necessary if using Protobuf well-known types:
prost-types = { version = "0.13.1", default-features = false }
此外,在 build.rs
中配置 prost-build
以输出 BTreeMap
而不是 HashMap
,以处理所有 Protobuf map
字段。
let mut config = prost_build::Config::new();
config.btree_map(&["."]);
当使用 edition 2015 时,可能需要在包含 prost
-生成的代码的 crate 中添加一个 extern crate core;
指令。
序列化现有类型
prost
使用自定义 derive 宏来处理类型的编码和解码,这意味着如果您的现有 Rust 类型与 Protobuf 类型兼容,您可以通过添加适当的 derive 和字段注释来序列化和反序列化它。
目前,关于添加注释的最好文档是查看上面的生成代码示例。
现有类型的 Tag 推断
Prost 自动推断结构体的 Tag。
字段按照指定的顺序进行标记,从 1
开始。
您可以通过在间隔后的第一个字段上指定 tag
属性来跳过已保留的标签或存在间隔的连续标签值。接下来的字段将从下一个数字开始顺序标记。
use prost;
use prost::{Enumeration, Message};
#[derive(Clone, PartialEq, Message)]
struct Person {
#[prost(string, tag = "1")]
pub id: String, // tag=1
// NOTE: Old "name" field has been removed
// pub name: String, // tag=2 (Removed)
#[prost(string, tag = "6")]
pub given_name: String, // tag=6
#[prost(string)]
pub family_name: String, // tag=7
#[prost(string)]
pub formatted_name: String, // tag=8
#[prost(uint32, tag = "3")]
pub age: u32, // tag=3
#[prost(uint32)]
pub height: u32, // tag=4
#[prost(enumeration = "Gender")]
pub gender: i32, // tag=5
// NOTE: Skip to less commonly occurring fields
#[prost(string, tag = "16")]
pub name_prefix: String, // tag=16 (eg. mr/mrs/ms)
#[prost(string)]
pub name_suffix: String, // tag=17 (eg. jr/esq)
#[prost(string)]
pub maiden_name: String, // tag=18
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Enumeration)]
pub enum Gender {
Unknown = 0,
Female = 1,
Male = 2,
}
Nix
prost 项目维护本地开发时的 flakes 支持。一旦您设置了 nix 和 nix flakes,您就可以运行 nix develop
来获取一个配置了所需依赖项的 shell,以编译整个项目。
功能标志
std
:启用与标准库的集成。为no_std
支持禁用此功能。默认启用此功能。derive
:启用与prost-derive
的集成。禁用此功能以减少编译时间。默认启用此功能。prost-derive
:已弃用。是derive
功能的别名。no-recursion-limit
:禁用递归限制。递归限制为 100,无法自定义。
常见问题解答
prost
是否可以作为 Serde 的序列化器实现?
可能不是,但我希望听到一位Serde专家对此事的看法。尝试使用Serde序列化Protobuf消息有两个问题。
- Protobuf字段需要一个编号的标签,但目前似乎在
serde
中并没有适合此功能的机制。 - Protobuf类型到Rust类型的映射不是一一对应的。因此,基于特质的调度方法不太适用。例如:六个不同的Protobuf字段类型对应Rust中的
Vec<i32>
:repeated int32
、repeated sint32
、repeated sfixed32
及其打包形式。
但是,可以在生成的类型上放置serde
derive标签,这样同一结构就可以同时支持prost
和Serde
。
- 我在MacOS上尝试运行
cargo test
时遇到了错误。
如果错误是关于缺少autoreconf
或其他类似的问题,你可能可以通过运行以下命令来解决:
brew install automake
brew install libtool
许可证
prost
是在Apache许可证(版本2.0)的条款下分发的。
有关详细信息,请参阅LICENSE。
版权所有2022 Dan Burkert & Tokio贡献者
依赖项
~175KB