当社には「サービスやプロダクトの品質向上につながるなら、新しい技術も積極的に試してみる」というカルチャーがあります。今回は、その中でも、近年「学びたい言語ランキング」で上位に入り、業界でも注目を集めているRust を取り入れた開発事例をご紹介します。
まだリリース前のプロジェクトなので一部詳細は伏せていますが、「技術選定の過程」と「実際に使ってみた所感」の二本立てでお届けします。その第1回となる本記事では、検討編としてRust導入に至った背景や他言語との比較、想定したリスクと対策についてまとめました。
Rustとは?
RustはMozillaが開発したシステムプログラミング言語です。メモリ安全性と高パフォーマンスを両立できるのが特徴で、私たちのプロジェクトでは「リアルタイムな通話処理」と「長期安定運用」を支える基盤として最適だと判断しました。
Rustの基本的な特徴
- ガーベジコレクションがないため、停止時間が予測可能で安定した処理が可能
- C/C++並みの高速性を持ちながら、コンパイル時に安全性を保証できる
- 所有権・借用・ライフタイムといった仕組みで不具合を構造的に防止できる
- CargoやRustupを利用することで依存関係の管理、クロスコンパイルなどを容易に行うことができる
当社でRustを導入した背景
当社のビジネスフォン向けSaaSの一部に、Rustを導入することとなりました。
電話サービスは、万が一止まってしまえば業務に大きな支障をきたします。また、音質が低下するだけでも業務効率に影響を与えてしまうため、通話を扱うプログラムには緻密な処理と高速な動作が求められます。
従来は C言語・Python・シェルスクリプト を組み合わせた仕組みで実装していましたが、以下の課題が目立つようになっていました。
・C言語:
メモリ解放漏れやスレッド競合による不具合が検出しづらい
・Python:
依存パッケージの配布が複雑で運用負荷が大きい
・シェルスクリプト:
複雑な処理の実装と保守性および拡張性の両立が難しい
複数言語が混在するためビルドやデプロイが複雑化し、変更の影響範囲が把握しづらい状況、かつ機能追加を重ねる中で設計上の限界が見えてきている状況でした。そのことから、設計を根本的に見直すことにしました。
言語選定のポイントと比較対象
新しい基盤を選ぶにあたり、次の要件を重視しました。
- 10年以上使える長期メンテナンス性
- 音声処理に必要な安定したパフォーマンス
- クロスコンパイルや異なるプラットフォームへの対応力
- バイナリデータや文字列解析のしやすさ
これらを軸に、通話制御を想定したうえでRust、Go、C++、Node.jsを比較検討しました。
Rust
特徴
- コンパイラによるメモリ安全性保証
- C/C++並みの高いパフォーマンス
- 非同期処理を言語レベルでサポート
- GCがないため予期しない遅延が発生しない
- Cargoによる依存管理やクロスコンパイルが容易
懸念点
- 所有権・ライフタイムなど学習コストが高い
- コンパイル時間が長め
- 新しい言語のためライブラリや実績が不足している領域もある
- 実行ファイルサイズが大きくなりがち
Go
特徴
- 書きやすく開発効率が高い
- 並行処理(goroutine)の強力なサポート
- GCによるリソース管理の安全性
懸念点
- GCによる予測困難なパフォーマンス低下のリスク
- 音声処理のような低レイテンシ必須領域では不利な場面も
C++
特徴
- 圧倒的な処理速度
- 既存のCコードを流用しやすい
- RAIIによる安全性向上
懸念点
- 書き方の自由度が高く、規約が必要
- 安全性確保には高度なスキルが必要
- 開発者依存で品質が大きく変わりやすい
Node.js
特徴
- 非同期処理を言語レベルでサポート
- クロスプラットフォーム開発が容易
- 複雑な処理を比較的簡単に記述可能
懸念点
- パフォーマンスは他言語に劣る傾向
- 実行環境導入の複雑さ
- マルチスレッドサポートが弱い
なぜRustなのか?
Rustを採用した最大の理由は、「安全性とパフォーマンスの両立」 にあります。コンパイラがメモリ安全性を保証してくれることにより、検出が難しいクラスの不具合を構造的に排除できます。また、GCがないため予期せぬパフォーマンス低下がなく、通話処理に求められる安定したレスポンスを確保できます。
例えばGoやNode.jsでは通話の制御という観点からはパフォーマンス面で不安を感じる部分があります。また、C++については、パフォーマンス面では優位性が非常に高いものの、コード品質の担保という点でRustと比較して弱いと判断しました。
Rustの採用において、今後の普及状況には不透明な部分もありますが、現時点では開発が活発で、Linuxや大手企業での導入事例も増えていることから、10年以上の長期利用にも耐えうる基盤になると判断しました 。
想定されたリスクとその対策
学習コスト
所有権やライフタイムの理解など、他の言語と比較した際に比較的学習コストが高く、気軽にメンバーをプロジェクトにアサインしにくくなるという点が懸念点として考えられました。
その対策として、ガイドライン、研修やサンプル実装の準備を行いました。
これにより、Rustをすぐに使いこなせるわけではありませんが、Rustの経験がないメンバーがプロジェクトにアサインされたとしても学習しやすい環境を整える努力をしました。
コンパイル時間
コンパイル時間が長くなり、開発スケジュールへの影響が懸念されましたが、ワークスペースの分割やインクリメンタルビルドを採用することで、コンパイル時間自体を短くする仕組みとし、可能な限りデメリットが低減されるような開発を行いました。
ライブラリの成熟度
比較的新しい言語であり、期待のライブラリの実績が少なかったり、そもそも存在しないという問題がありました。
この部分に関してはプロジェクト内でライブラリ実装を行う方針としました。
一方で、OSに標準搭載され広く利用されているライブラリについては、FFIを介して活用しています。
ただし、FFIには unsafe コードを含むため、ライブラリ仕様を精査したうえで、Rust側で適切にラップし、安全性を確保するよう努めました。
Rust導入を機に行った改善
ドキュメントの自動生成
Rustではマクロを利用することで柔軟なメタプログラミングが可能です。
これを活用し、システム内部で利用する設定ファイルのドキュメントをコード内のコメントから自動生成する仕組みを導入しました。
この仕組みにより、ドキュメントを手動で更新する手間を削減し、コードとドキュメントの乖離を防ぐことができるようになりました。
パフォーマンスの最適化
Rustは適切にコードを書くことで、C言語に匹敵する高いパフォーマンスを発揮します。
一方で、Rustのコンパイラによるチェックは安全性を優先する設計となっており、必ずしも最適なコードが生成されるとは限りません。
そのため必要に応じてunsafeなコードを利用するのですが、このとき適切にカプセル化を行い、どのような状況でもunsafeなコードの影響が外部に及ばないよう配慮することで、危険性を最小限に抑えつつ、パフォーマンスの最大化を図りました。
まとめ
今回のRust導入は、複雑化していた技術スタックを整理し、リアルタイム性が求められる通話処理を安定して支えるための再設計でした。学習コストやビルド時間といったハードルはあるものの、コンパイル時の安全性と予測可能な性能は非常に大きな価値があります。
次回の記事(実装編)では、実際のモジュール構成や非同期処理の実装、テスト戦略についてさらに具体的に紹介します。
採用へのメッセージ
トビラシステムズには、チームで学び合い、分かち合う文化があります。真剣にものづくりへ向き合いながらも、時にはリラックスした会話を交えつつ、より良いプロダクトを一緒につくっています。Rustや新しい技術に挑戦したい方は、ぜひ画面最下部のエントリーからご連絡ください。
ここまでお読みいただき、ありがとうございました。