S-JIS[2024-09-28/2024-10-12] 変更履歴

Rust prostメモ

Rustのprostクレートのメモ。


概要

prostは、Rustでprotobufを扱うクレート(ライブラリーおよびビルドツール)。

protoファイルからRustのソースコードを生成することが出来る。

コンパイル時(および実行時)に使うprostと、ビルド時(protoファイルからRustのソースを生成する)に使うprost-buildがあり、両方を使用する。


ビルド時には、裏でprotoc(protobufのコンパイラー)が実行される。
なので、開発環境にprotocをインストールしておく必要がある。

本来なら、protoc単体でprotoファイルから各種言語のソースコードを生成できるのだが、バージョン28.2時点ではRustには正式対応していないようだ。
(試しに実行してみたが、よく分からないエラーが出たので断念したorz)
なので、prost-buildを使うのが、今のところ一番確実。


protocのインストール

protocは以下の手順でインストールする。

  1. protocの実行ファイルのアーカイブをprotobufのReleasesからダウンロードする。
  2. ダウンロードしたアーカイブファイルを適当な場所に解凍する。
  3. 実行ファイルの場所(例:「〜/protoc-28.2-win64/bin」)にパスを通しておく。(環境変数PATHに追加しておく)

prost-build実行時にprotocが見つからないとエラーになる。

エラーメッセージには「パスを通すか環境変数PROTOCに実行ファイルの場所を記述する」と出ているのだが、自分が試した範囲では環境変数PROTOCは認識してくれなかったので、パスを通しておく方が確実だと思う。


prostの例

prostおよびprost-buildの例は、Snazzyを見るのが分かりやすい。


まず、Cargo.tomlにprostとprost-buildを追加する。

プロジェクト/Cargo.toml:

〜

[dependencies]
prost = "0.13"

[build-dependencies]
prost-build = "0.13"

次に、protoファイルをプロジェクト内に配置する。

Snazzyでは「プロジェクト/src」直下にitems.protoを置いているが、自分としてはsrcの下はrsファイルだけにしたいので、「プロジェクト/proto」というディレクトリーに置くことにした。

プロジェクト/proto/items.proto

syntax = "proto3";

package snazzy.items;

// A snazzy new shirt!
message Shirt {
  enum Size {
    SMALL = 0;
    MEDIUM = 1;
    LARGE = 2;
  }

  string color = 1;
  Size size = 2;
}

プロジェクト内にprotoファイルを配置したら、build.rsにそのprotoファイルの場所を記述する。
(build.rsは、ビルド時に実行されるRustソースコード)

プロジェクト/build.rs

extern crate prost_build;

fn main() {
    prost_build::compile_protos(
        &["proto/items.proto"],
        &["proto/"]
    ).unwrap();
}

compile_protos()の第1引数にprotoファイルを指定する。
配列なので、複数ファイルを指定可能。protoファイルが複数ある場合は、全て指定する。
指定するパスは、プロジェクトディレクトリーからの相対パス。

ディレクトリーを指定することは出来ない。(サブディレクトリーを探索してくれると便利だと思うんだけど)

なお、protoファイルの中で他のprotoファイルをimportしている場合は、importされるprotoファイルは(compile_protos()で指定しなくても )自動的に読み込まれるようだ。

第2引数は、protoファイルを置いてあるディレクトリー。
(なんでファイルの他にディレクトリーを指定しないといけないのかは謎…)


そして、ビルドを実行する。

$ cargo build

問題が無ければ、build.rsで指定したprotoファイルが読まれ、Rustのソースコードが生成される。

生成されるファイルは、「target/debug/build/プロジェクト名-よく分からない十六進数/out/snazzy.items.rs」。
この十六進数はハッシュ値か何かだと思うが、ビルドする度に数値が異なるディレクトリーが増えてくる。どれが最新かはよく分からない^^;
生成されるファイル名は、protoファイルの中に記述されていたpackageのようだ。


最後に、生成されたrsファイルモジュールに加える。
(モジュールとして追加しないと、生成された構造体や列挙型を使うことが出来ない)

プロジェクト/main.rsまたはlib.rs:

pub mod snazzy {
    pub mod items {
        include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
    }
}

snazzy.items.rsの中にはShirt構造体が定義されているが、「mod snazzy { mod items { }}」で囲むと、「snazzy::items::Shirt」として使用できるようになる。


ビルドエラー

生成されたrsファイル内の構造体の中にエラーがあると、「::prost::Message」の箇所がエラーになる。

error[E0412]: cannot find type `Size2` in module `super`
 --> C:\\example\ffi\ffi-example-rust\target\debug\build\ffi-example-rust-3f216621b1ac0cb6\out/snazzy.items.rs:3:28
  |
3 | #[derive(Clone, PartialEq, ::prost::Message)]
  |                            ^^^^^^^^^^^^^^^^ not found in `super`  

このエラーは、以下のような状態で発生した。

プロジェクト/proto/items2.proto:(新設)

syntax = "proto3";

package snazzy;

enum Size2 {
  SMALL = 0;
  MEDIUM = 1;
  LARGE = 2;
}

プロジェクト/proto/items.proto:(一部追加)

syntax = "proto3";

package snazzy.items;

import "items2.proto";	←追加

// A snazzy new shirt!
message Shirt {
  enum Size {
    SMALL = 0;
    MEDIUM = 1;
    LARGE = 2;
  }

  string color = 1;
  Size size = 2;

  Size2 size2 = 4;	←追加
}

プロジェクト/lib.rs:

pub mod snazzy {
    pub mod items {
        include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
    }
    // items2が入っていない
}

つまり、protoファイルには問題は無いし、生成されたrsファイルにも問題は無いのだが、
items2.protoから生成されたsnazzy.rsがモジュールに含まれていないので、ビルド対象になっていない。
そのため、それに依存しているsnazzy.items.rsでエラーが発生したというわけ。


この場合、items2.protoから生成されたrsファイルをモジュールに含めてビルドし直せば解決する。

pub mod snazzy {
    include!(concat!(env!("OUT_DIR"), "/snazzy.rs"));
    pub mod items {
        include!(concat!(env!("OUT_DIR"), "/snazzy.items.rs"));
    }
}

しかし原因が分かってみればエラーになって当然の内容だったけど、
エラー発生個所が「::prost::Message」だし、エラーメッセージが「not found in `super`」で、意味不明だ…orz

まぁその前に「cannot find type `Size2` in module `super`」というメッセージも出ているので、Size2が見つからない→Size2はitems2.protoで定義している、というところから推測できるかもしれないけど…。

また、エラーメッセージの「super」の意味は、items.protoのパッケージが「snazzy.items」でitems2.protoのパッケージが「snazzy」だから、パッケージ (Rust的にはモジュール)上の親(super)ということだと思う…。


Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま