在rust中使用Protobuf
文章目录
前言
Google的Protobuf(全称Google Protocol Buffer),是一种高效轻便的结构化数据存储方式,可用于(数据)通信协议、数据存储等。优势这里就不多说了,基本上主流的程序语言都支持,性能也很强。下面讲讲在RUST里怎么用。
一、使用方法
在RUST中使用Protobuf比较简单,主要有三个步骤:
第一步:编制消息定义文件(.proto文件);
第二步:生成消息序列化文件(.rs文件);
第三步:在程序中使用对应的方法对消息进行序列化和反序列化。
其中,消息定义文件的编制规范不是本文重点,这里不做介绍,有需要的话可以查阅Google相关文档。
二、生成消息序列化文件的几种途径
RUST的文档里列举了Protobuf的三种使用途径(官方文档: protobuf_codegen):
1.通过protoc命令行配合protoc-gen-rust插件;
2.通过protobuf_codegen包使用纯RUST;
3.通过protobuf_codegen包使用protoc。
几种方式各有优劣,不存在说哪个更好,根据自己的实际情况选择就行。
其中,第一种方法需要下载protoc程序以及protoc-gen-rust插件,在命令行下执行生成RUST文件,后两种方法则通过在RUST程序中引入protobuf_codegen包来达到同样的目的。具体方法如下:
1.protoc+protoc-gen-rust插件
protobuf的安装这里不重点介绍,默认大家已经安装。如果没有安装的话资源链接在这里,大家可以自行下载编译。(Git地址: protobuf)
在OS-X以及Ubuntu上也可以通过如下方式安装:
Windows下成功编译后可生成protoc.exe文件,在命令行执行“protoc --version”命令可以显示版本号。
目前protoc直接可以支持的语言有C++,java,js,c#,Python,Ruby,Objective-C,go,Dart,PHP,暂时还不包括RUST,所以我们需要安装插件。
插件的安装也很简单,直接在命令行执行:“cargo install protobuf-codegen”即可,当然,前提是你已经安装了cargo。
OK!激动人心的时刻终于来了!命令行窗口再次执行如下命令:
protoc -I=
S
R
C
D
I
R
−
−
r
u
s
t
o
u
t
=
SRC_DIR --rust_out=
SRCDIR−−rustout=DST_DIR $SRC_DIR/xxx.proto
其中:
$SRC_DIR: .proto 所在的源目录;
–cpp_out: 生成 c++ 代码;
$DST_DIR: 生成代码的目标目录;
xxx.proto: 要针对哪个 proto 文件生成接口代码。
以Google提供的addressbook.proto消息定义文件为例,生成前文件目录如下:
命令行窗口执行命令:
protoc -I=. --rust_out=./ ./addressbook.proto
文件夹变为:
生成了两个文件:addressbook.rs和mod.rs。
相较起来,在RUST中使用protobuf-codegen包的第二和第三种方法就简单很多。
2.protobuf_codegen+pure RUST
Cargo.toml中添加依赖:
[dependencies]
protobuf-codegen="3"
protobuf="3"
main.rs文件:
fn main() {
Codegen::new()
.pure()
.out_dir("src/protos")
.input("src/protos/addressbook.proto")
.include("src/protos")
.run_from_script();
println!("Generate ok!");
}
其中,out_dir为rs文件输出目录,input为proto文件路径,include为proto文件所在目录。
在项目路径下执行:
cargo run,搞定!
3.protobuf_codegen+protoc
与方法2类似。
Cargo.toml中添加依赖:
[dependencies]
protobuf-codegen="3"
protobuf="3"
protoc-bin-vendored-win32="3.0.0"
protoc-bin-vendored="3.0.0"
main.rs文件:
use protobuf_codegen::Codegen;
fn main() {
Codegen::new()
.protoc()
.protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
.out_dir("src/protos/1")
.input("src/protos/addressbook.proto")
.include("src/protos")
.run_from_script();
println!("Generate ok!");
}
cargo run,搞定!
三、在程序中使用
1.创建工程``
命令行运行:
cargo new pb_rust
2.拷贝生成的rs消息
将前面生成的mod.rs和addressbook.rs拷贝至src文件夹:
3.修改Cargo.toml文件
[package]
name = "pb_rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
protobuf-codegen="3"
protobuf="3"
3.修改main.rs文件
测试方法为:创建一个Person结构体,序列化为字节数组,再通过字节数组反向构造出Person结构体。
这里可以先看看proto文件中结构体的定义:
syntax = "proto3";
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
repeated PhoneNumber phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
main.rs代码如下:
mod addressbook;
use protobuf::Message;
fn main() {
let mut p=addressbook::Person::new();
p.id=1000;
p.email=String::from("person@163.com");
p.name=String::from("person");
let mut ph=addressbook::PhoneNumber::new();
ph.number=String::from("18899996666");
ph.type_=addressbook::PhoneType::MOBILE.into();
p.phones.push(ph);
let out_bytes: Vec<u8> = p.write_to_bytes().unwrap();
let msg=addressbook::Person::parse_from_bytes(&out_bytes).unwrap();
println!("id:{}",msg.id);
println!("name:{}",msg.name);
println!("email:{}",msg.email);
println!("phone_number:{}",msg.phones[0].number);
//println!("phone_type:{}",msg.phones[0].type_.value());
if addressbook::PhoneType::MOBILE==addressbook::PhoneType::from(msg.phones[0].type_.unwrap())
{
println!("phone_type:MOBILE");
}
}
4.编译执行文件
cargo run之后的结果:
总结
在c++里用过,感觉在RUST里面使用确实清爽很多。