Cargo.tomlにtonicとtonic-buildだけを並べた依存指定は、0.14系では通りません。0.14でコーデックとビルド統合が別クレートへ切り出され、依存の書き方そのものが変わりました。この記事では2026年9月1日時点のcrates.io実測値をもとに、tonic 0.14.6系での依存構成、protoからのコード生成、TLSとインターセプタ、Go・Pythonクライアントとの相互接続確認までを実装順に並べます。プロトコルの仕組みそのものはgRPCの仕組みとRESTとの違いに譲り、ここではRust側の工程だけを扱います。
まとめ|0.14系で先に決める3点と落ちやすい1点
着手前に決めるのは3つです。tonicのfeatureをどこまで有効にするか、生成コードをリポジトリに入れるか毎回build.rsで作るか、そしてTLS実装にtls-ringとtls-aws-lcのどちらを使うか。いずれも後から変えると、Cargo.tomlだけでなくCIのイメージと証明書の配布手順まで巻き戻ります。
落ちやすいのはビルド環境側です。生成を担うprost-buildはProtocol Buffersコンパイラを同梱せず、PATH上のprotocを探します。手元では通ってCIだけ落ちる、という失敗の大半がこれです。回避の経路は3つあり、後述のh2で条件ごとに分けます。
結論を先に置きます。2026年9月時点で本番に入れるならtonic 0.14.6系です。gRPC公式のRust実装は同じリポジトリでプレビュー公開されましたが、採用判断の材料はまだ足りていません。
tonic 0.14系のクレート分割|0.13までの依存指定が通らない理由
0.14系の最大の変更は、Protocol Buffersとの結合をtonic本体から外したことです。tonicはコーデックに依存しない土台になり、prostを使う部分が独立したクレートへ移りました。日本語の解説記事は0.12から0.13の構成で書かれたものが多く、そのままCargo.tomlへ写すと生成コードのコンパイルで型が見つかりません。
tonicとtonic-prostの分離|実行時依存に足す2クレート
実行時側に必要なのはtonicとtonic-prostの2つです。前者がHTTP/2上のgRPC実装とサーバ・チャネルを持ち、後者がprostで直列化するコーデックを提供します。公式リポジトリのexamplesも、この2つとprostを並べる構成に変わりました。
| クレート | 版(実測) | 役割 | 置く場所 |
|---|---|---|---|
| tonic | 0.14.6 | gRPC本体とサーバ | dependencies |
| tonic-prost | 0.14.6 | prostコーデック | dependencies |
| prost | 0.14.4 | メッセージ型の直列化 | dependencies |
| tonic-prost-build | 0.14.6 | protoからの生成 | build-dependencies |
| tokio | 1.53.1 | 非同期ランタイム | dependencies |
版はいずれも2026年9月1日にcrates.ioで確認した安定版です。0.14系はパッチ番号が揃っているため、片方だけ上げる運用は避けて同じマイナー版で束ねます。
tonic-prost-buildをbuild-dependenciesへ置く生成構成
生成側ではtonic-prost-buildをbuild-dependenciesに置く構成です。0.13まではtonic-buildが生成もprost連携も担っていましたが、0.14ではtonic-buildがコーデック非依存の骨組みだけを持ち、prost向けの生成は分離後のクレートが担当します。build.rsの呼び出しもtonic_prost_build::configure()に変わります。
ここを取り違えると、エラーは「関数が無い」という素直な形では出ません。生成そのものは走らず、include_proto!の展開先が空になり、サービス構造体が見つからないという遠い場所の型エラーになります。build.rsを先に直すのが早道です。
tokioのrt-multi-threadとmacrosを要求する実行時前提
tonicはtokio上で動くため、ランタイムのfeatureは明示が必要です。examplesが要求しているのはrt-multi-threadとmacrosで、前者が複数スレッドのスケジューラ、後者が#[tokio::main]の展開を担います。既定featureのままtokioを追加すると、起動時にランタイム不在で落ちます。
非同期構文に不慣れなら、先にRustのasync/await構文の基本を押さえたほうが読み進めやすくなります。サービス実装は、返り値がFutureになるtraitの実装がそのまま並ぶ形だからです。
tonic側の既定featureはrouter・transport・codegenの3つです。transportはサーバとチャネルの両方を含む束ねなので、クライアント専用のバイナリならchannelだけに絞ってビルド時間を削れます。
protoからの生成工程|protocの入手とinclude_protoの取り込み
生成はcargo buildの一部として走ります。protoファイルを置き、build.rsで対象と探索パスを指定し、生成物をコード側で取り込む。この3段のどこで失敗したかを切り分けられれば、詰まる時間は短くなります。protoの記法はProtocol Buffersのproto定義の書き方にまとめてあります。
configureとcompile_protosで生成物をOUT_DIRへ出す手順
build.rsの中心はconfigure()で作った設定に対するcompile_protosの呼び出しです。第1引数がコンパイル対象のprotoファイル、第2引数が探索の基点となるディレクトリで、後者を省くとimportの解決に失敗します。
- protoファイルを置くディレクトリを決め、パッケージ名を確定させる
- build.rsで
configure()に生成オプションを積む compile_protosに対象ファイルと探索の基点を渡す- 生成物はcargoが用意する
OUT_DIR配下に出る - コード側は
include_proto!でモジュールへ展開する
設定で使う頻度が高いのは3つです。クライアントだけ欲しいときにbuild_serverを偽にする指定、リフレクション用の記述子を書き出すfile_descriptor_set_path、生成コードへ属性を足すserver_attribute。生成物をリポジトリへ入れる運用ならout_dirで出力先を移します。
protocがPATHに無いとビルドが落ちる条件と回避の3経路
生成の実体はprost-buildが呼び出すProtocol Buffersコンパイラです。prost-buildのドキュメントは、PATH上のprotocを探し、見つからなければcompile_protosが失敗すると明記しています。「Rustならprotocのインストールは不要」と書かれた記事は、コンパイラを同梱していた古い版の挙動を指しています。
- PATHへ入れる:コンテナのビルドステージでコンパイラをインストールする
- 環境変数で指す:
PROTOCに実行ファイルの位置を渡す - ソースから作る:
protobuf-srcで用意し設定へ渡す
受託案件でCIをコンテナ化しているなら、1つ目を選びます。ビルドイメージにコンパイラを固定版で焼き込めば、開発者の手元と生成結果が揃うからです。開発者ごとにバージョンが違う状態は、生成コードの差分がプルリクエストに紛れ込む原因になります。
include_protoに渡すパッケージ名と生成物の対応関係
コード側の取り込みはinclude_proto!にprotoのpackage名を渡す形で、ファイル名ではありません。間違えると生成物が見つからず、しかもbuild.rsは正常終了するため、原因の見えない不在エラーになります。
サービス名からの命名規則も押さえておきます。protoでGreeterというサービスを定義すると、サーバ側はgreeter_serverモジュール、クライアント側はgreeter_clientモジュールへ生成されます。protoのサービス名を変えるとRust側のモジュール名まで動くため、公開APIの命名は最初に固めてください。
サーバとクライアントの実装形|登録の順序とチャネル再利用の基準
生成が通れば書く量は多くありません。サーバはtraitの実装とビルダーへの登録、クライアントは接続の生成と呼び出しだけです。決めるべきはその外側、接続の寿命と締め切りの与え方になります。
async_traitでのサービス実装とadd_serviceでの登録手順
生成されたtraitを実装するとき、メソッドは非同期になるため#[tonic::async_trait]を付けます。引数はRequestで包まれ、返り値はResponseとStatusのResultです。メタデータやTLSの接続情報はextensionsから取り出す形になっており、引数の形を崩さずに横断的な情報を運べます。
起動はServer::builder()にサービスをadd_serviceで足し、待受アドレスへserveする流れです。複数サービスを1プロセスへ載せる場合も同じビルダーに積みます。ヘルスチェックとリフレクションはtonic-health・tonic-reflectionを追加サービスとして載せる設計で、本体のfeatureでは切り替えません。
クライアントのチャネル再利用とデッドライン設定を決める運用基準
クライアント側で最初に決めるのは接続の寿命です。gRPCのチャネルは接続プールに相当するため、呼び出しごとに作り直すとHTTP/2のハンドシェイクを毎回やり直します。生成されたクライアント型はクローンが安価に作れる構造で、1つのチャネルを共有したまま複数タスクへ配る前提の設計になっています。
締め切りは呼び出し側で必ず与えます。無指定だと相手が応答を止めたときにタスクが残り続けます。同期的な参照系なら数百ミリ秒から数秒、バッチ寄りの処理なら分単位を上限に置き、サーバ側の処理時間の実測値から逆算してください。0.14.6ではクライアントのエンドポイントに最大フレームサイズの指定が加わり、大きなメッセージを扱う経路で調整の幅が広がりました。
TLSとインターセプタの構成|featureの選び分けと認証の差し込み位置
ここから先は本番前提の設定です。tonicのTLSはrustlsベースで、暗号ライブラリの選択がfeatureとして表に出ています。認証はインターセプタで受けます。
tls-ringとtls-aws-lcの選び分けとルート証明書のfeature
暗号処理の実装はtls-ringとtls-aws-lcの2択です。内部用の_tls-anyを直接指定してはいけません。どちらか一方を選び、クライアントとして外部に出るなら、検証に使うルート証明書の入手先も選びます。
| feature | 役割 | 選ぶ場面 |
|---|---|---|
| tls-ring | 暗号実装をringに | 既定の第一候補 |
| tls-aws-lc | 暗号実装をaws-lcに | AWS準拠が要件 |
| tls-native-roots | OSの証明書を使う | 社内CAが混在 |
| tls-webpki-roots | 同梱の証明書を使う | 配布先を固定したい |
サーバ側は証明書と鍵からIdentityを作り、ServerTlsConfigとしてビルダーへ渡します。クライアント証明書で相手を確かめる構成にすると、サービス実装側では接続情報がextensionsに入るため、どの証明書で来た呼び出しかをメソッド内で判別できます。0.14.6ではサーバ証明書の検証器を差し替えるAPIが公開され、独自CAでの検証をコードから制御しやすくなりました。
with_interceptorでメタデータ検証を差し込む位置と例外
認証トークンの検証は、生成されたサーバ型のwith_interceptorで包む形が定石です。インターセプタはリクエストを受け取り、メタデータを見て、通すかStatusを返して止めるかを決めます。判定結果を後段へ渡すときは、独自の型をextensionsへ入れるとサービス実装から読めます。
ここで例外を1つ作ってください。ヘルスチェックとリフレクションの経路までトークン必須にすると、ロードバランサの生存確認が全滅します。認証を挟むのは業務サービスの登録分だけに限り、監視系のサービスは別にadd_serviceしてください。
エラー返却でStatusコードを選ぶ際の対応付けと実装判断の指針
gRPCのエラーはHTTPのステータスとは別系統です。tonicではStatusを返すだけなので実装は簡単ですが、コード選定を雑にすると呼び出し側のリトライ設計が崩れます。特に再試行してよい系と、してはいけない系の区別は最初に決めます。詳細な使い分けはgRPCのステータスコード16種の意味とリトライ可否にまとめました。
付帯情報を構造化して返す場合、tonic-typesが追加対象です。バリデーション違反のフィールド名を機械可読な形で返せるため、フロント側の表示制御が楽になります。文字列メッセージへ詰め込む実装は、多言語対応の段で必ず破綻します。
Go・Pythonクライアントとの相互接続|protoの共有とずれの検出
Rustでサーバを書く案件は、たいてい呼び出し側が別言語です。言語が増えると、protoの所有者とバージョン整合の管理が実装の質を決めます。
proto所有者を1か所に決める運用とバージョン整合の確認手順
protoファイルをRustリポジトリに置いてコピーを配る運用は、3か月で崩れます。所有者を1つのリポジトリに定め、各言語はそこを参照して生成する形に寄せてください。受託開発なら、この所有権と変更手順を要件定義の段で文書化します。後から決めると、互換性のない変更が誰の承認もなく入る事故が起きます。
Python側の生成手順と型スタブの扱いはgRPC Pythonの実装手順に整理してあります。同じprotoから各言語で生成する構成では、フィールド番号の再利用が全言語を巻き込む破壊的変更になる点だけ、共通のルールとして共有しておいてください。Go側の生成手順とクライアント接続APIの移行はgRPC Goの実装手順にまとめています。
相互接続で最初に確認すべき実務上の3項目|疎通・圧縮・締め切り
確認は順序を守ると早く終わります。まず素の疎通、次に圧縮の合わせ、最後に締め切りとエラーの伝播。逆順で進めると、圧縮の不一致を疎通不能と誤認して時間を捨てます。
圧縮は落とし穴になりやすい箇所です。tonicはgzip・deflate・zstdをfeatureで有効化する設計で、既定では有効になっていません。相手の言語実装が既定でgzipを送る設定なら、featureを足さないと受け側で失敗します。締め切りはクライアント側の指定がサーバへ伝わるため、Rust側の処理でその締め切りを見て早期に打ち切る実装にしておくと、無駄な計算が減ります。
採用の境界|tonicを選ぶ条件と公式gRPC-Rustを待つ判断
最後に判断を置きます。技術選定の場では「Rustで書けるか」ではなく「Rustで書く理由があるか」を先に問うべきで、gRPCの実装が可能かどうかは判断材料になりません。可能だからです。
grpc crate 0.9.0のプレビューを本番へ入れない条件
2026年5月28日、gRPC公式のRust実装がgrpcクレートの0.9.0としてプレビュー公開されました。tonicのリポジトリはhyperium配下からgrpc組織のgrpc-rustへ移り、公式実装とtonicが同じリポジトリに並んでいます。将来の本線がこちらへ寄る可能性は高いといえます。
それでも、いま本番へ入れるべきではありません。理由は3つです。初期プレビューの表明であること、tonicにあるweb・health・reflectionといった周辺クレートの対応が揃っていないこと、そして受託開発では保守期間中のAPI安定が納品条件に直結すること。新規案件はtonic 0.14.6系で組み、公式実装は検証環境で追随するに留める。この線引きを2026年内は動かさない判断でよいと考えます。
ただしtonic側にも注意書きがあります。公式リポジトリのREADMEは、master側で破壊的変更を準備中であると明記しています。参照するサンプルは0.14系のブランチに固定してください。masterのコードをそのまま写すと、リリース済みの版でコンパイルが通りません。
RustでgRPCを選ばない3場面の判断基準と代替手段の置き方
見送るべき場面を条件付きで挙げます。第一に、ブラウザから直接呼ぶ必要がある構成。gRPCはブラウザから素で呼べないためプロキシ層が増え、Rustを選んだ利点が運用の複雑さで消えます。第二に、チームにRustの保守要員が1人しかいない場合。gRPCの実装以前に、その1人が抜けた時点で保守が止まります。第三に、呼び出し頻度が日次バッチ程度で、レイテンシもスループットも要件になっていない場合。この条件ならHTTPのJSON APIで十分で、生成工程とCIのコンパイラ管理を持ち込む価値がありません。
逆に採用してよい条件は明確です。サーバ間の内部通信で呼び出し回数が多い、CPU効率とメモリ使用量が費用に直結する、スキーマを型で縛りたい。この3つが揃うなら、tonicは有力な選択肢になります。双方向の常時接続が要件に入る場合は、gRPCストリーミング4方式の使い分けまで含めて設計してください。
受託開発でproto所有と生成タイミングを決める実務上の取り決め
実装以外で決めておく取り決めが3つあります。protoの所有リポジトリと変更承認者。生成コードをコミットするかビルド時に作るか。そしてProtocol Buffersコンパイラの版をどこで固定するか。この3点を契約前に握らないと、複数言語のクライアントが増えた段階で結合テストが止まります。
スキーマ設計から複数言語のクライアント実装、CIの整備までまとめて任せたい場合は、API開発・システム連携でご相談ください。既存のHTTP APIを残したままgRPCの経路を足す移行設計にも対応しています。
よくある質問
RustでgRPCを実装する際に、依存構成とビルド環境まわりで受ける質問をまとめました。
tonic-buildとtonic-prost-buildはどちらを使いますか?
0.14系でprostの生成を使うならtonic-prost-buildです。tonic-buildはコーデックに依存しない骨組みだけを持つ形に変わりました。0.13までの記事に載っているtonic_build::compile_protosだけの構成は、0.14系では生成が期待どおりに動きません。build.rsの呼び出し先とbuild-dependenciesの記述を、参照する記事の版に合わせて読み替えてください。
Rustのビルドにprotocのインストールは必要ですか?
必要です。prost-buildはPATH上のprotocを探し、見つからなければ生成に失敗します。位置が違う場合は環境変数PROTOCで指すか、設定から実行ファイルのパスを渡します。ソースから用意するprotobuf-srcも選択肢です。CIでは版を固定してビルドイメージに焼き込むのが確実です。
tonicのTLSでtls-ringとtls-aws-lcはどう選びますか?
特段の要件がなければtls-ringを選びます。tls-aws-lcはAWS準拠の暗号ライブラリを使う要件がある場合の選択肢です。両方を同時に有効にはできません。内部用の_tls-anyを直接指定するのも誤りです。クライアントとして外部へ出るなら、ルート証明書をOSから取るか同梱物を使うかも併せて決めます。
tonicで書いたサーバをGoのクライアントから呼べますか?
呼べます。同じprotoから各言語のコードを生成すれば疎通します。実務でつまずくのは圧縮方式の不一致です。tonicは圧縮をfeatureで有効化する設計のため、相手が既定でgzipを送る場合はfeatureの追加が必要になります。締め切りとエラーコードの伝播も、疎通確認のあとに個別に検証してください。
公式のgRPC-Rustが出たらtonicから移行すべきですか?
2026年9月時点では移行の判断材料が足りません。公式実装は同年5月28日に0.9.0として初期プレビューが公開された段階で、周辺クレートの対応も揃っていません。新規案件はtonic 0.14.6系で組み、公式実装は検証環境で追う形が現実的です。両者は同じリポジトリで開発されているため、移行経路の情報はそこから追えます。
関連記事
- gRPCとは?仕組み・RESTとの違い・UDP/TCPの扱いを実装例で解説:本記事の前提となるプロトコル側の仕組みとトランスポートの扱い
- Protocol Buffersとは?protoファイルの書き方と採用判断:生成のもとになるproto定義の記法とスキーマ互換の考え方
- gRPC Pythonの実装|スタブ生成からasync・FastAPI併存まで:相互接続の相手側になるPython実装の生成手順
- gRPCストリーミングとは?4方式の使い分けと切断対策:unaryの先へ進むときのストリーミング設計と切断対策
- Rustにおけるasync/await構文の基本とその使い方:tonicが前提とする非同期処理の基礎とランタイムの位置づけ
- gRPC C#の実装|Grpc.AspNetCoreのビルド時生成とホスティング制約:同じprotoからC#側を組むときのビルド時生成と配置の条件