(2024年1月時点)RustのWebフレームワークの簡単な比較

はじめに

こんにちは、エンジニアのクロ(@kro96_xr)です。

エンジニアと言いながら最近コードを書く機会が減っていたので、年末年始にRustにチャレンジしていました。
まだ全然理解しきれていないという自覚はありつつ、今回はRustの主要なWebフレームワークについて簡単に調査、比較していきたいと思います。 間違いなどありましたらDM等でご指摘いただけると幸いです。

なお、情報は全て調査時点(2024/1/8)のものとなります。

Frameworkの候補

フレームワークの比較をしてくれているリポジトリがあるので、この情報を元にピックアップしてみます。

github.com

人気のフレームワーク

名称 Repos Docs スター数 最新バージョン Activity
actix-web Repo Doc 19.4k 4.4.1 181/year
axum Repo Doc 14.3k 0.7.3 307/year
Rocket Repo Doc 22.5k 0.5.0 176/year

Githubのスター数も多く、調査中によく名前を見かけました。
ただ、actix-web以外はバージョン1.0.0以下であることを考えるとプロダクトに使用するのは躊躇われるかもしれません。

中堅フレームワーク

名称 Repos Docs スター数 最新バージョン Activity
warp Repo Doc 8.8k 0.3.6 27/year
poem Repo Doc 3k 2.0.0 154/year
salvo Repo Doc 2.5k 0.63.1 660/year

先程の3つほどスター数は多くないですが、それぞれ独自の特徴や強みからスター数を伸ばしています。
warpは最新のリリースが2023/9/27でActivityも少ないのが気になりますね。一方でsalvoはActivityだけの判断になりますが勢いが凄いなという印象です。

これから期待のフレームワーク

名称 Repos Docs スター数 最新バージョン Activity
pavex Repo Doc 1.2k 0.1.0 -

Pavexは先ほどのリポジトリの表には載っていませんが、他のフレームワークとは異なるアプローチをしているとのことでこれからに期待です。
以下のような日本語記事もありますので参考にしてみてください。今回の記事では詳細については触れません。

Pavex – Rust API構築のための新しいWebフレームワーク | DevelopersIO

GithubリポジトリのStar数の推移

続いて、以下のサイトからStar数の推移を見てみます。

GitHub Star History

歴史としてはRocketがいちばん長く、次に出てきたactix-webとともに継続して人気なようです。
warpはこの中では3番目に古いですが、後続のaxumに抜かれています。
axumがこの勢いで伸びていくとactix-webやRocketに追いつくことになりそうですね。

各フレームワークの特徴とコードサンプル

続いて各フレームワークの特徴を簡単にまとめつつ、HTTPサーバを立ち上げて"Hello, world!"を返すエンドポイントを実装する簡単なサンプルコードを記載します。
エラーハンドリング等未考慮なのであくまで参考程度にご覧ください。

actix-web

axumやRocketと違いメジャーバージョンになっており、安定性を担保していると言えます。 技術的には、Tokioを基盤にしつつも独自の抽象化とクレートを持つことで結合を弱めているとのことです。 書き心地に関しては、HelloWorldレベルでは他のフレームワークと大きな差は感じられませんでした。 歴史も長く様々なクレートやドキュメント、サンプルがあり、第一の選択肢となりそうです。

use actix_web::{get, App, HttpResponse, HttpServer, Responder};
use serde_json::json;

#[actix_web::main]
pub async fn main() -> std::io::Result<()> {
    println!("launching server...");
    http_start("0.0.0.0", 8080).await
}

async fn http_start(addr: &str, port: u16) -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello)
    })
    .bind((addr, port))?
    .run()
    .await
}

#[get("/hello")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().json(json!({"message": "Hello, world!"}))
}

axum

axumは前述の通り、近年伸びているフレームワークです。 特徴としては、Tokioプロジェクトの一部としてエコシステムに組み込まれていることがあります。 また、他のフレームワークではマクロが出てくるのに対し、axumはマクロを使用せずに実装することができ、初学者でも理解しやすいと思いました。

現時点でバージョン1未満ということで破壊的変更などがあるかもしれないのが少々不安ですが、今後も継続して伸びていくと思われます。

use axum::{routing::get, Router, response::{IntoResponse, Json}, http::StatusCode};
use tokio::net::TcpListener;
use serde_json::json;

#[tokio::main]
async fn main() {
    let app = router();

    let listener = listener("0.0.0.0", 8081);
    axum::serve(listener.await, app).await.unwrap();
}

fn router() -> Router {
    Router::new().
        route("/hello", get(hello))
}

async fn listener(addr: &str, port: u16) -> TcpListener {
    let addr_port = format!("{}:{}", addr, port);
    TcpListener::bind(addr_port).await.unwrap()
}

async fn hello() -> impl IntoResponse {
    (StatusCode::OK, Json(json!({"message": "Hello, world!"})))
}

Rocket

Rocketは前述の通り古くから存在するフレームワークですが、近年ではactix-webやaxumに遅れを取っているようです。思想として「全てを含む」ことがあるようで必要な機能を全てRocketの管理下においています。 結果として、リリースサイクルが他フレームワークと比較して遅くなることがあるようで、そのあたりが遅れをとっている要因かもしれません。実際にバージョン0.5の開発に2年以上かかっていたようです。

一方で必要な機能が一通りRocket側で用意されているので手軽にWebアプリケーションを開発したい場合にはいいかもしれません。以下のサンプルコードでもrocketのみに依存しています。

use rocket::*;
use rocket::serde::{Serialize, json::Json};
use rocket::http::Status;

#[derive(Serialize)]
struct Message {
    message: String,
}

#[rocket::main]
async fn main() {
    println!("launching server...");
    let config = configure("0.0.0.0", 8082);
    rocket::build()
        .configure(config)
        .mount("/", routes![hello])
        .launch()
        .await
        .unwrap();
}


fn configure(addr: &str, port: u16) -> rocket::Config {
    rocket::Config {
        address: addr.parse().unwrap(),
        port: port,
        ..rocket::Config::default()
    }
}

#[get("/hello")]
fn hello() -> (Status, Json<Message>) {
    let data = Message {
        message: "Rocket: Hello, world!".to_string(),
    };
    (Status::Ok, Json(data))
}

warp

warpに関しては他のフレームワークと書き心地が全く違いました。「フィルター」を用いてリクエストを処理するアプローチで、そのコンセプトを理解して実装する必要があります。

The main concept in warp is the Filter, which allows composition to describe various endpoints in your web service. Besides this powerful trait, warp comes with several built in filters, which can be combined for your specific needs.

個人的にはHelloWorldを実装する時点で一番混乱したフレームワークです。関数型のアプローチは面白いと思うのですが、自分が不慣れなこともあり正直理解しきれていません。前述の通り直近のActivityも少なく今後が少し心配です。

use std::convert::Infallible;
use warp::{Filter, Reply};
use serde::Serialize;

#[derive(Serialize)]
struct Message {
    message: String,
}


async fn hello() -> Result<impl warp::Reply, Infallible> {
    let message = "warp: Hello, world!".to_string();
    Ok(warp::reply::json(&message))
}

fn routes() -> impl Filter<Extract = impl Reply, Error = warp::Rejection> + Clone {
    warp::path!("hello")
        .and(warp::get())
        .and_then(hello)
}

#[tokio::main]
async fn main() {
    println!("launching server...");
    let routes = routes();
    warp::serve(routes)
        .run(([0, 0, 0, 0], 8085))
        .await;
}

poem

書き心地自体は他のフレームワークと大差ないと感じましたが、poemエコシステム内でgRPCやLambda、OpenAPIに対応したクレートを実装しており、Web開発のニーズを満たそうという意識を感じました。 同時期に出たaxumにスター数でも差が開けられていたり日本語記事が全然見つからなかったりと存在感はまだまだかもしれませんが、このまま開発が続いていけば非常に面白そうです。

poem/poem-openapi/README.md at master · poem-web/poem · GitHub

use poem::{get, handler, listener::TcpListener, web::Path, web::Json, IntoResponse, Route, Server};
use serde::Serialize;

#[derive(Serialize)]
struct Message {
    message: String,
}

#[handler]
fn hello() -> Json<Message> {
    Json(Message {
        message: "poem: Hello, world!".to_string(),
    })
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    let app = Route::new().at("/hello", get(hello));
    Server::new(TcpListener::bind("0.0.0.0:8084"))
        .run(app)
        .await
}

salvo

salvoはシンプルながらパワフルな特徴を持つと謳っており、実際にベンチマークでは上位につけています。

TechEmpower Framework Benchmarks

HelloWorldでの比較なので他のフレームワークと比べてどれほどシンプルかしづらいく、機能を持たせたWebアプリケーションで比較してみたいですね。別の機会があれば踏み込んでみたいと思います。

use salvo::prelude::*;
use serde::Serialize;

#[derive(Serialize)]
struct Message {
    message: String,
}

#[tokio::main]
async fn main() {
    println!("launching server...");
    tracing_subscriber::fmt().init();

    let router = Router::new()
        .push(Router::new().path("hello").get(hello));
    let acceptor = TcpListener::new("0.0.0.0:8083").bind().await;
    Server::new(acceptor).serve(router).await;
}

#[handler]
async fn hello(res: &mut Response) {
    let data = Message {
        message: "Salvo: Hello, world!".to_string(),
    };
    res.render(Json(data));
}

おわりに

以上、簡単にではありますがRustのWebフレームワークについて調査、比較してきました。何かの参考になれば幸いです。 Rustはライブラリの整備状況などまだ不十分な部分もありますが、トレンドとして注目されてきており(遅いですが)私自身継続してRustについて学んでいきたいと思います。

参考

Rust で Web バックエンド開発をはじめる | CyberAgent Developers Blog

Best Rust Web Frameworks to Use in 2023

GitHub - flosse/rust-web-framework-comparison: A comparison of some web frameworks and libs written in Rust

Pavex – Rust API構築のための新しいWebフレームワーク | DevelopersIO

async対応版Rocket、v0.5を試してみる - paild tech blog

RustでWebアプリケーションを作る - CADDi Tech Blog