使用旧的Rust 2015
0.0.1 |
|
---|
#74 在 #prost
72KB
1.5K SLoC
PROST!
prost
是 Protocol Buffers 的 Rust 语言实现。 prost
从 proto2
和 proto3
文件生成简单、惯用的 Rust 代码。
与其他协议缓冲区实现相比,prost
- 通过利用 Rust 的
derive
属性,生成简单、惯用和易于阅读的 Rust 类型。 - 在生成的 Rust 代码中保留来自
.proto
文件的注释。 - 通过添加属性,允许现有的 Rust 类型(非从
.proto
生成)进行序列化和反序列化。 - 使用
bytes::{Buf, BufMut}
抽象来序列化,而不是使用std::io::{Read, Write}
。 - 在将生成的代码组织到 Rust 模块时,尊重 Protobuf 的
package
声明。 - 在反序列化过程中保留未知的枚举值。
- 不包括对运行时反射或消息描述符的支持。
在 Cargo 项目中使用 prost
首先,将 prost
及其公共依赖项添加到您的 Cargo.toml
中(有关当前版本,请参阅 crates.io)
[dependencies]
prost = <prost-version>
prost-derive = <prost-version>
# Only necessary if using Protobuf well-known types:
prost-types = <prost-version>
bytes = <bytes-version>
向 Cargo 项目添加 .proto
编译的最推荐方法是使用 prost-build
库。有关更多详细信息和方法示例,请参阅 prost-build
文档。
生成的代码
prost
使用 proto2
或 proto3
语法从源 .proto
文件生成 Rust 代码。 prost
的目标是最简化生成的代码。
包
目前,所有与 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 消息中的字段被转换为 Rust 中的对应类型的公共结构体字段。
标量值
标量值类型按以下方式转换
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
值转换为枚举类型的方法。该 enum
类型不会直接用作字段,因为 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
。
许可证
prost
在 Apache 许可证(版本 2.0)的条款下分发。
有关详细信息,请参阅 LICENSE。
版权所有 2017 Dan Burkert
依赖项
~390KB