Golangで出力したprotobufバイナリをRustで読み込む

ここ三回くらいprotobufの記事を書いてきましたが、Goばかりだったので、Rustで読み込んでみました。

事前準備:Goでバイナリを出力する

user.protoを以下のように定義します。

syntax = "proto3";
package user;

message User {
  string name = 1;
  int32 age = 2;
}

Go用にprotocする

❯ protoc -I=./ --go_out=./ user.proto

user.pb.goが生成されます。

バイナリを書き出す

以下のユーザをprotobufのバイナリとしてgo_user.binを書き出します。

フィールド
Name Alice
Age 20

出力用のコードは以下の通りです。

package main

import (
    "fmt"
    "io/ioutil"
    "log"

    pb "github.com/cipepser/protobuf-sample/rust-protobuf-example/user"
    "github.com/golang/protobuf/proto"
)

func main() {
    p := &pb.User{
        Name: "Alice",
        Age:  20,
    }

    out, err := proto.Marshal(p)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(out)
    if err := ioutil.WriteFile("./go_user.bin", out, 0644); err != nil {
        log.Fatalln("Failed to write:", err)
    }
}

Rustで読み込む

いよいよ本題のRustです。

Cargo.tomlの設定

Rustでprotobufを扱うためにrust-protobufを使います。 Cargo.tomldependenciesprotobufを記載します。 READMEにも書いてありますが、bytesクレートを使っているのでbytesも記載します。

[dependencies]
protobuf = { version = "~2.0", features = ["with-bytes"] }

2019/08/03追記
stepancheg/rust-protobuf: Rust implementation of Google protocol buffersに、version2未満がサポート対象外になったと書いてあるので、追記現在では以下のように記載します。

[dependencies]
protobuf = { version = "2", features = ["with-bytes"] }

Rust用にprotocする

user.protoは、Goでprotocしたときと同じものです。

❯ protoc --rust_out src/ user.proto

以下のようなメッセージが出る場合は、protoc-gen-rustがないのでインストールしましょう。

protoc-gen-rust: program not found or is not executable
--rust_out: protoc-gen-rust: Plugin failed with status code 1

以下でインストール出来ます(Cargo.tomlに書いてもいいのかも)。

❯ cargo install protobuf-codegen

protocがうまくいけば、src/user.rsがgenerateされます。

読み込む

ファイル読み込み、User型にmerge、標準出力するまでのコードは以下のとおりです。

extern crate protobuf;

mod user;

use user::User;
use std::fs::File;
use std::io::{BufReader};
use protobuf::{CodedInputStream, Message};

fn main() {
    let file = File::open("./go_user.bin").expect("fail to open file");
    let mut buffered_reader = BufReader::new(file);
    let mut cis = CodedInputStream::from_buffered_reader(&mut buffered_reader);

    let mut u = User::new();
    u.merge_from(&mut cis).expect("fail to merge");

    println!("Name: {}", u.get_name());
    println!("Age: {}", u.get_age());
}

実行すると、以下のようにNameAgeが読み取れていることがわかります。

/usr/local/bin/cargo run --color=always --package rust-protobuf-example --bin rust-protobuf-example
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/rust-protobuf-example`
Name: Alice
Age: 20

Process finished with exit code 0

References