0.5.1 |
|
---|---|
0.5.0 |
|
#69 in #prost
89KB
2K SLoC
PROST!
prost
是 Protocol Buffers 的 Rust 语言实现。 prost
从 proto2
和 proto3
文件生成简单、惯用的 Rust 代码。
与其他 Protocol Buffers 实现相比,prost
- 通过利用 Rust 的
derive
属性,生成简单、惯用且可读的 Rust 类型。 - 保留来自
.proto
文件的注释在生成的 Rust 代码中。 - 允许通过添加属性来序列化和反序列化现有的 Rust 类型(不是从
.proto
生成的)。 - 使用
bytes::{Buf, BufMut}
抽象进行序列化,而不是使用std::io::{Read, Write}
。 - 在组织生成的代码到 Rust 模块时,尊重 Protobuf 的
package
指示器。 - 在反序列化过程中保留未知的枚举值。
- 不包括对运行时反射或消息描述符的支持。
在 Cargo 项目中使用 prost
首先,将 prost
及其公共依赖项添加到您的 Cargo.toml
中(有关当前版本,请参阅 crates.io)
[dependencies]
prost = <prost-version>
bytes = <bytes-version>
# Only necessary if using Protobuf well-known types:
prost-types = <prost-version>
将 .proto
编译添加到 Cargo 项目的推荐方法是使用 prost-build
库。有关更多详细信息和方法示例,请参阅 prost-build
文档。
生成的代码
prost
使用 .proto
文件(采用 proto2
或 proto3
语法)生成 Rust 代码。其目标是使生成的代码尽可能简单。
包
使用 prost
的所有 .proto
文件都必须包含一个 package
指令。 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
类型,并提供辅助方法将 i32
值转换为枚举类型。枚举类型不直接用作字段,因为 Protobuf 规范要求枚举值是 '开放的',并且必须能够解码未识别的枚举值。
字段修饰符
根据 Protobuf 版本,Protobuf 标量值和枚举消息字段可以有修饰符。修饰符会改变 Rust 字段的对应类型
.proto 版本 |
修饰符 | Rust 类型 |
---|---|---|
proto2 |
optional |
Option<T> |
proto2 |
required |
T |
proto3 |
default | T |
proto2 /proto3 |
repeated | Vec<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[1]
pub struct Foo {
pub widget: Option<foo::Widget>,
}
pub mod foo {
pub enum Widget {
Quux(i32),
Bar(String),
}
}
oneof
字段总是被包裹在 Option
中。
[1] 已省略注释以清晰起见。有关完整示例,请参阅下面。
服务
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, Debug, PartialEq, Message)]
pub struct Person {
#[prost(string, tag="1")]
pub name: String,
/// Unique ID number for this person.
#[prost(int32, tag="2")]
pub id: i32,
#[prost(string, tag="3")]
pub email: String,
#[prost(message, repeated, tag="4")]
pub phones: Vec<person::PhoneNumber>,
}
pub mod person {
#[derive(Clone, Debug, PartialEq, Message)]
pub struct PhoneNumber {
#[prost(string, tag="1")]
pub number: String,
#[prost(enumeration="PhoneType", tag="2")]
pub type_: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Enumeration)]
pub enum PhoneType {
Mobile = 0,
Home = 1,
Work = 2,
}
}
/// Our address book file is just one of these.
#[derive(Clone, Debug, PartialEq, Message)]
pub struct AddressBook {
#[prost(message, repeated, tag="1")]
pub people: Vec<Person>,
}
序列化现有类型
prost
使用自定义 derive 宏来处理类型的编码和解码,这意味着如果你的现有 Rust 类型与 Protobuf 类型兼容,你可以通过添加适当的 derive 和字段注解来序列化和反序列化它。
目前,关于添加注解的最好文档是查看上面的生成代码示例。
现有类型的标签推断
Prost 会自动为结构体推断标签。
字段按指定的顺序进行标签,从 1
开始。
你可以通过在空隙后的第一个字段上指定 tag
属性来跳过已保留的标签或标签值之间存在差距的标签。以下字段将从下一个数字开始进行顺序标签。
#[derive(Clone, Debug, PartialEq, Message)]
struct Person {
pub id: String, // tag=1
// NOTE: Old "name" field has been removed
// pub name: String, // tag=2 (Removed)
#[prost(tag="6")]
pub given_name: String, // tag=6
pub family_name: String, // tag=7
pub formatted_name: String, // tag=8
#[prost(tag="3")]
pub age: u32, // tag=3
pub height: u32, // tag=4
#[prost(enumeration="Gender")]
pub gender: i32, // tag=5
// NOTE: Skip to less commonly occurring fields
#[prost(tag="16")]
pub name_prefix: String, // tag=16 (eg. mr/mrs/ms)
pub name_suffix: String, // tag=17 (eg. jr/esq)
pub maiden_name: String, // tag=18
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Enumeration)]
pub enum Gender {
Unknown = 0,
Female = 1,
Male = 2,
}
常见问题解答
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。
版权 2017 Dan Burkert
依赖项
~6.5MB
~132K SLoC