なぜウェブアプリ開発に Rust を使うのか

こんにちは!スタッフエンジニアの @kenkoooo です。入社してから早いもので3年が経過しました。相変わらず平均年収2000万円を目指してやっていますが、まだ道半ばです。引き続き頑張ります。

estie は10を超えるウェブサービスを提供していますが、フロントエンドは Next.js で統一され、バックエンドは大半が Rust という構成になっています。また、バックエンドが裏側で叩いているマイクロサービスも大半が Rust で実装されています。

Rust を使っている会社は増えてきつつあるかと思いますが、ここまでどっぷり Rust にベットしている会社は多くないのではないかと思います。この記事では、estie でどのように Rust を使っているか、どのようなメリット・デメリットがあるかを紹介します。

estie での Rust の使い方

ウェブフレームワーク

estie のウェブサービスのバックエンドは HTTP で呼び出されるため、それらをいい感じにハンドリングするウェブフレームワークを使っています。Rust のウェブフレームワークとしては有名なものとしては axumactix-web があり、estie では actix-web を使っています。 estie で Rust を採用し始めた3年前は axum がまだ出たばかりで、async/await に対応したウェブフレームワークの代表格が actix-web だったため、そのまま actix-web を使い続けています。普通に使う分には axum も actix-web も使い勝手はそこまで変わらないので、いま改めて選ぶなら axum でもいいかもしれません。

GraphQL

API としてはバックエンドもマイクロサービスも GraphQL を使っています。GraphQL のライブラリとしては juniperasync-graphql が有名で、estie では async-graphql を使っています。

juniper で dataloader を使うためには dataloader という別のクレートもインストールする必要がありますが、async-graphql には dataloader が組み込まれています。estie のプロダクトの性質上、dataloader は不可欠だったので、dataloader と一体で開発されている async-graphql を選ぶことにしました。

また、@oneOf というまだ公式にはなっていない GraphQL の仕様がありますが、フロントエンド側のライブラリではサポートされており、これが使えれば TypeScript からの API の呼び出しがより楽になります。この仕様が async-graphql には実装されているというのも選定の理由の1つです。

use async_graphql::*;

#[derive(OneofObject)]
enum SearchBy {
    Stations(SearchByStations),
    Prefectures(SearchByPrefectures),
}

#[derive(InputObject)]
struct SearchByStations {
    station_ids: Vec<StationId>,
}

#[derive(InputObject)]
struct SearchByPrefectures {
    prefecture_ids: Vec<PrefectureId>,
}

struct Query {}

#[Object]
impl Query {
    async fn search_buildings(&self, by: SearchBy) -> Vec<Building> {
        // input に対して match で処理を分岐できる
    }
}

Rust のメリット

Rust の特徴として、コンパイラによる静的なチェックが強力であることが知られています。このことから「動かすまでが大変」として避けられていることもありますが、チーム開発においては大きなメリットであると考えています。

estie には Rust 経験者が入社してくることはほぼ無く、estie で初めて Rust を書くという人がほとんどです。また、Rust に限らずバックグラウンドも様々で、チーム内でのスキルセットにもばらつきがあります。コンパイラが型や構文だけでなく、所有権などについてもチェックしてくれることで、チーム内でスキルにばらつきがあっても一定の品質が担保された状態でプルリクが作られるため、人間のコードレビューの負担を減らすことができると考えています。

また、昨今は AI がコードを書く機会も増えてきました。AI によるコード生成と Rust コンパイラによるチェックを組み合わせてサイクルを回すことで、高速に高品質なコードを量産できると考えています。過去のモデルでは Rust コンパイラが出すエラーメッセージを LLM が正しく把握できずに CI と格闘し続けるというシーンもありましたが、Claude 3.7 Sonnet 以降のモデルではコンパイルが通せずに挫折してしまうケースはほとんど見られなくなりました。LLM がコードを書くことによってレビューの負担が増していくと思われますが、この負担を軽減するのにも Rust コンパイラによる強力なチェックは有効であると考えています。

Rust のデメリット

実際の開発サイクルの中で Rust を使う最も大きいデメリットとして、コンパイルに時間がかかることが挙げられます。手元の開発環境は incremental compilation が効いているので問題ありませんし、ビルドキャッシュを使うことで CI の実行時間も一定削減することはできるものの、Python などのスクリプト言語や Go などのコンパイルが高速な言語と比較して、CI を待っている時間が長い印象はあります。estie のプロダクト開発スタイルは、小さいイテレーションで高速に開発を進めるというもので、1日に数回本番リリースがあることも多いので、デプロイの待ち時間が長いというのは常に悩みの種です。

まとめ

estie では Rust をどっぷり使っています。パフォーマンスを追求したり、低レイヤーを実装したりするわけではなく、Rust の用途としてはライトな使い方ではありますが、estie のビジネスを支える重要な技術の一つになっています。

Rust が好きな人も、そうでない人も(Rust を使わないプロダクトもあります)、ぜひご入社ください。動く Rust のコードはいっぱいあるので、Rust を触ったことがなくても動かしながら学べます。

hrmos.co

© 2019- estie, inc.