Rust 做後端:什麼情況才值得

Rust 的 web framework 在選型上有一個很重要的前提問題:你為什麼要用 Rust 而不是 Go?

Go 有 GC(garbage collection),但 GC pause 在大多數 API 場景裡幾乎感覺不到。Rust 沒有 GC(靠 ownership / borrow checker 管記憶體),代價是學習曲線高出 Go 一個量級。

Rust 的 web 場景真正有明確優勢的是:

  • 系統層級服務:proxy、load balancer、CDN edge,對記憶體和 latency 有極致要求
  • Wasm 部署:Rust → WebAssembly,在瀏覽器 / Cloudflare Workers 跑
  • 嵌入式或資源受限環境:沒有 JVM / CPython runtime overhead
  • 和既有 Rust codebase 整合:系統已經用 Rust,順勢在同一個 codebase 加 HTTP 層

如果你只是要寫一個一般的 JSON API,Go 的 Gin 或 Chi 通常是更務實的選擇。


Axum:Tokio 生態的標準選擇

Axum(2021,由 Tokio 團隊維護)是目前 Rust web framework 的主流選擇:

use axum::{
    extract::{Path, State},
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
 
#[derive(Serialize, Deserialize)]
struct User {
    id: i64,
    name: String,
    email: String,
}
 
#[derive(Deserialize)]
struct CreateUserDto {
    name: String,
    email: String,
}
 
async fn get_user(
    Path(id): Path<i64>,         // ← 型別安全的 path extraction
    State(db): State<DbPool>,    // ← DI via State extractor
) -> Result<Json<User>, StatusCode> {
    let user = db.find_user(id).await
        .map_err(|_| StatusCode::NOT_FOUND)?;
    Ok(Json(user))
}
 
async fn create_user(
    State(db): State<DbPool>,
    Json(dto): Json<CreateUserDto>,  // ← 自動 deserialize + 型別安全
) -> Result<(StatusCode, Json<User>), StatusCode> {
    let user = db.create_user(dto).await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
    Ok((StatusCode::CREATED, Json(user)))
}
 
#[tokio::main]
async fn main() {
    let db_pool = DbPool::new("postgres://...").await.unwrap();
 
    let app = Router::new()
        .route("/users/:id", get(get_user))
        .route("/users", post(create_user))
        .with_state(db_pool);
 
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Axum 最大的優點是和 Tower middleware 生態完全相容——Tower 是 Tokio 生態的 middleware 標準,所有 Tower-compatible 的 middleware 都可以直接用在 Axum 上(rate limit、tracing、timeout 等)。

缺點:Rust 的 ownership 和 lifetime 讓某些本來簡單的操作(例如在 middleware 裡存取 request context)變得需要仔細考慮型別。


Actix-web:Actor 模型 + 極致效能

Actix-web 基於 Actor 模型(類似 Erlang OTP),在效能 benchmark 上持續是最快的 HTTP framework 之一:

use actix_web::{web, App, HttpServer, HttpResponse};
use serde::{Deserialize, Serialize};
 
#[derive(Serialize)]
struct User { id: i64, name: String }
 
async fn get_user(path: web::Path<i64>) -> HttpResponse {
    let id = path.into_inner();
    HttpResponse::Ok().json(User { id, name: "Alice".to_string() })
}
 
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/users/{id}", web::get().to(get_user))
    })
    .bind("0.0.0.0:3000")?
    .run()
    .await
}

Actix-web 的問題:它自己實作了 async runtime,不完全依賴 Tokio,這讓它和 Tokio 生態的整合有些摩擦。如果你需要 Tokio 生態(sqlx、redis、tower),Axum 通常是更順暢的選擇。

適合:純效能導向、不需要深入 Tokio 生態整合的場景。


Rocket:最接近 Rails 的 Rust 寫法

Rocket 以「直覺的 API 設計」為目標,用 macro 大量簡化寫法:

#[macro_use] extern crate rocket;
 
use rocket::serde::json::Json;
 
#[derive(serde::Serialize)]
struct User { id: i64, name: String }
 
#[get("/users/<id>")]
fn get_user(id: i64) -> Json<User> {
    Json(User { id, name: "Alice".to_string() })
}
 
#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![get_user])
}

Rocket 的 macro-heavy 寫法讓程式碼量很小,但 macro 展開的錯誤訊息有時候很難 debug。

適合:Rust 初學者想快速上手;API 不複雜;不需要深入 Tokio 整合。


選擇決策

主流選擇 + Tokio 生態完整整合?
  → Axum

純效能最高,不需要 Tokio 整合?
  → Actix-web

Rust 初學者 / 偏好 Rails-like 簡潔寫法?
  → Rocket

Rust web framework 的選擇在 2026 是相對清楚的:大部分新的 Rust web 專案會選 Axum,因為 Tokio 是 Rust async 生態的主幹,和 Tokio 相容的生態整合是長期成本最低的路。proto proto2661047/b2e/rust-web 如果有的話,設計模式以 Axum 為主。


延伸閱讀