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 為主。
