Rust na backendzie webowym: Actix Web vs Axum - porównanie i pytania rekrutacyjne 2026
Praktyczne porównanie Actix Web 4.14 i Axum 0.8 dla webowego rozwoju w Rust w 2026. Architektura, benchmarki TechEmpower Round 23, doświadczenie deweloperskie i pytania rekrutacyjne na stanowiska backendowe Rust.

Ekosystem webowy Rust osiągnął w 2026 roku punkt, w którym wybór frameworka backendowego przestał być eksperymentem, a stał się decyzją architektoniczną o wymiernych konsekwencjach produkcyjnych. Dwa frameworki zdominowały krajobraz: Actix Web w wersji 4.14 oraz Axum w wersji 0.8. Każdy z nich reprezentuje odmienną filozofię projektową, od modelu wątków po architekturę middleware, i każdy z nich pojawia się coraz częściej w pytaniach technicznych na rozmowach kwalifikacyjnych dotyczących stanowisk backendowych.
Actix Web 4.14 zapewnia 10-15% wyższą przepustowość dzięki modelowi przypiętych wątków (pinned-thread). Axum 0.8 oferuje natywne async traits, pełną kompatybilność z ekosystemem Tower i prostszą krzywą nauki. W przypadku nowych projektów webowych w Rust rozpoczynanych w 2026 roku Axum stanowi rozsądny wybór domyślny, o ile ekstremalnie wysokie wymagania przepustowości nie przemawiają za Actix Web.
Architektura runtime: pinned-thread kontra work-stealing
Fundamentalna różnica między Actix Web a Axum leży w sposobie zarządzania wątkami i planowania zadań asynchronicznych.
Actix Web stosuje model pinned-thread: tworzy N izolowanych, jednowątkowych instancji runtime Tokio, gdzie N odpowiada liczbie fizycznych rdzeni procesora. Każde przychodzące połączenie jest obsługiwane przez dokładnie jeden wątek przez cały cykl życia żądania. Dane nie migrują między wątkami, co eliminuje narzut synchronizacji i rywalizację o linie pamięci podręcznej (cache-line contention). Ten model sprawdza się szczególnie dobrze przy obciążeniach o jednorodnym charakterze, gdzie każde żądanie wymaga zbliżonej ilości pracy.
Axum opiera się na jednym współdzielonym, wielowątkowym runtime Tokio z algorytmem work-stealing. Tokio dynamicznie przenosi zadania między wątkami w celu równomiernego rozłożenia obciążenia. Framework został zaprojektowany przez zespół Tokio jako referencyjny przykład wykorzystania ich runtime, każdy handler to zwykła funkcja async, a warstwa middleware bazuje na traicie Service z biblioteki Tower.
Poniższe dwa przykłady ilustrują minimalny serwer HTTP w każdym z frameworków.
use actix_web::{web, App, HttpServer, HttpResponse};
async fn health() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({ "status": "ok" }))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/health", web::get().to(health))
})
.bind("0.0.0.0:8080")?
.run()
.await
}use axum::{Router, Json, routing::get};
use serde_json::{json, Value};
async fn health() -> Json<Value> {
Json(json!({ "status": "ok" }))
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/health", get(health));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}Nawet na poziomie minimalnych przykładów widoczne są różnice projektowe. Actix Web wykorzystuje dedykowane makro #[actix_web::main] i wymaga jawnego zwracania std::io::Result. Axum korzysta ze standardowego #[tokio::main], a handler zwraca typowany ekstraktor Json<Value> zamiast ręcznie konstruowanego HttpResponse. Struktura Router w Axum pełni rolę centralnego rejestru tras, podczas gdy Actix Web buduje aplikację za pomocą App::new() z łańcuchowymi wywołaniami .route().
Porównanie wydajności: benchmarki Actix Web 4.14 vs Axum 0.8
Dane z TechEmpower Round 23 (styczeń 2026) dostarczają najbardziej wiarygodnego porównania. Oba frameworki plasują się w czołówce we wszystkich kategoriach.
| Metryka | Actix Web 4.14 | Axum 0.8.9 |
|---|---|---|
| Plaintext (req/s) | ~7 100 000 | ~6 200 000 |
| Serializacja JSON (req/s) | ~1 200 000 | ~1 050 000 |
| Zapytanie DB (req/s) | ~190 000 | ~175 000 |
| Zużycie pamięci (hello world) | ~8 MB | ~6 MB |
| Latencja P99 (JSON) | 1,1 ms | 1,3 ms |
Actix Web konsekwentnie utrzymuje przewagę 10-15% w zakresie przepustowości we wszystkich kategoriach. Axum odpowiada niższym zużyciem pamięci dzięki współdzielonemu runtime Tokio. Dla porównania: oba frameworki przewyższają typowy serwer Go (net/http) 2-3-krotnie, a Node.js 5-8-krotnie na identycznym sprzęcie.
W praktyce ta różnica ma realne znaczenie wyłącznie w scenariuszach ekstremalnej przepustowości: serwowanie reklam programatycznych, potoki ingestii danych analitycznych w czasie rzeczywistym czy bramki handlu algorytmicznego. Dla standardowego REST API obsługującego kilkanaście tysięcy żądań na sekundę wąskim gardłem i tak pozostanie baza danych lub komunikacja z usługami zewnętrznymi, nie sam framework.
Wzorce ekstraktorów i obsługa żądań
System ekstraktorów decyduje o tym, jak framework parsuje przychodzące żądania HTTP i udostępnia ich komponenty w handlerach. Axum 0.8 wprowadził istotne ulepszenia w tym obszarze, usunięto makro #[async_trait] na rzecz natywnych async traits (stabilnych od Rust 1.75) i dodano OptionalFromRequestParts do eleganckiej obsługi opcjonalnych parametrów.
use axum::{
extract::{Path, Query, State, Json},
routing::get,
Router,
};
use serde::Deserialize;
use std::sync::Arc;
#[derive(Deserialize)]
struct Pagination {
page: Option<u32>, // defaults to None if missing
per_page: Option<u32>,
}
// State shared across handlers
struct AppState {
db_pool: sqlx::PgPool,
}
// Axum 0.8: /{id} syntax (replaced /:id)
async fn get_user(
State(state): State<Arc<AppState>>,
Path(user_id): Path<i64>,
Query(pagination): Query<Pagination>,
) -> Json<serde_json::Value> {
let page = pagination.page.unwrap_or(1);
// Query database using state.db_pool
Json(serde_json::json!({
"user_id": user_id,
"page": page
}))
}use actix_web::{web, HttpResponse};
use serde::Deserialize;
#[derive(Deserialize)]
struct Pagination {
page: Option<u32>,
per_page: Option<u32>,
}
struct AppState {
db_pool: sqlx::PgPool,
}
async fn get_user(
state: web::Data<AppState>,
path: web::Path<i64>,
query: web::Query<Pagination>,
) -> HttpResponse {
let user_id = path.into_inner();
let page = query.page.unwrap_or(1);
HttpResponse::Ok().json(serde_json::json!({
"user_id": user_id,
"page": page
}))
}W Axum ekstrakcja odbywa się przez destrukturyzację krotek w sygnaturze funkcji: State(state), Path(user_id). W Actix Web parametry są opakowywane w typy generyczne (web::Data<T>, web::Path<T>, web::Query<T>), a dostęp do wartości wewnętrznych wymaga wywołania .into_inner(). Oba podejścia zapewniają bezpieczeństwo typów w czasie kompilacji, jednak składnia Axum jest bliższa idiomatycznemu Rust i czyta się bardziej naturalnie.
Parametry ścieżki zmieniły składnię z /:id na /{id} w Axum 0.8 (poprzez matchit 0.8). To dopasowanie do składni OpenAPI. Literalne nawiasy klamrowe zapisuje się jako podwójne: {{ dla literalnego {.
Middleware: ekosystem Tower kontra system Actix Web
Architektura middleware stanowi prawdopodobnie najważniejszą praktyczną różnicę między tymi frameworkami.
Axum w pełni integruje się z biblioteką Tower, korzystając z traitów Service i Layer. Oznacza to, że każdy middleware napisany dla ekosystemu Tower, ograniczniki szybkości, warstwy tracing, kompresja HTTP, mechanizmy uwierzytelniania, działa z Axum bez żadnych modyfikacji. Co więcej, ta kompozycyjność wykracza poza protokół HTTP: ten sam middleware może opakowywać usługi gRPC realizowane przez Tonic.
use axum::{
Router, middleware,
routing::get,
extract::Request,
response::Response,
};
use tower_http::{
cors::CorsLayer,
compression::CompressionLayer,
trace::TraceLayer,
};
use std::time::Instant;
// Custom middleware as a plain async function
async fn timing_middleware(
request: Request,
next: middleware::Next,
) -> Response {
let start = Instant::now();
let response = next.run(request).await;
let duration = start.elapsed();
tracing::info!("Request took {:?}", duration);
response
}
fn build_router() -> Router {
Router::new()
.route("/api/data", get(|| async { "ok" }))
.layer(middleware::from_fn(timing_middleware))
.layer(CompressionLayer::new())
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http())
}Actix Web posiada własny, niezależny system middleware oparty na traitach Transform i Service (nie tych z Tower). Middleware napisany dla ekosystemu Tower nie jest bezpośrednio kompatybilny, wymaga napisania warstwy adaptacyjnej lub przepisania od podstaw.
Dla zespołów pracujących już z ekosystemem Tower, na przykład łączących usługi HTTP z gRPC przez Tonic lub korzystających z Hyper jako klienta HTTP, kompatybilność middleware Axum stanowi istotną przewagę operacyjną. Dla zespołów budujących izolowaną usługę HTTP własny system middleware Actix Web oferuje porównywalną funkcjonalność, choć kosztem mniejszej przenoszalności między projektami.
Actix Web 4.14 wprowadził walidację middleware tras, która powoduje panic jeśli handlery są dodawane po opakowaniu middleware, co pozwala wykryć błędy konfiguracji przy starcie aplikacji zamiast w runtime.
Gotowy na rozmowy o Rust?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Integracja z bazą danych: SQLx jako wspólny mianownik
SQLx to asynchroniczny zestaw narzędzi SQL dla Rust, który waliduje zapytania w czasie kompilacji. Kluczowa cecha: warstwa bazodanowa pozostaje identyczna niezależnie od tego, czy aplikacja korzysta z Actix Web, czy z Axum.
use sqlx::PgPool;
// This struct works identically with Actix Web and Axum
#[derive(sqlx::FromRow, serde::Serialize)]
struct User {
id: i64,
email: String,
created_at: chrono::NaiveDateTime,
}
// sqlx::query_as! validates against a live DB at compile time
async fn find_user_by_email(
pool: &PgPool,
email: &str,
) -> Result<Option<User>, sqlx::Error> {
sqlx::query_as!(
User,
"SELECT id, email, created_at FROM users WHERE email = $1",
email
)
.fetch_optional(pool)
.await
}Makro query_as! łączy się z działającą instancją PostgreSQL w trakcie kompilacji i weryfikuje nazwy kolumn, typy danych oraz samo istnienie tabel. Błąd w nazwie kolumny powoduje błąd kompilacji, a nie awarię w środowisku produkcyjnym. Kompromisem jest konieczność dostępu do bazy danych w procesie budowania, w pipeline'ach CI rozwiązaniem jest polecenie sqlx prepare, które generuje metadane zapytań offline i zapisuje je w katalogu .sqlx commitowanym do repozytorium.
Obsługa błędów: IntoResponse kontra ResponseError
Wzorce obsługi błędów ujawniają odmienne podejścia projektowe obu frameworków. Axum opiera się na traicie IntoResponse w połączeniu ze standardowym typem Result. Actix Web korzysta z dedykowanego traitu ResponseError.
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
// Define application-level errors
enum AppError {
NotFound(String),
DatabaseError(sqlx::Error),
ValidationError(String),
}
// Convert errors into HTTP responses
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
AppError::NotFound(msg) => (
StatusCode::NOT_FOUND, msg
),
AppError::DatabaseError(_) => (
StatusCode::INTERNAL_SERVER_ERROR,
"Internal server error".to_string(),
),
AppError::ValidationError(msg) => (
StatusCode::BAD_REQUEST, msg
),
};
(status, Json(serde_json::json!({ "error": message })))
.into_response()
}
}
// Handlers return Result<T, AppError>
async fn get_user(
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Json<serde_json::Value>, AppError> {
if id <= 0 {
return Err(AppError::ValidationError(
"ID must be positive".to_string()
));
}
Ok(Json(serde_json::json!({ "id": id })))
}Podejście Axum pozwala na naturalne wykorzystanie operatora ? i implementacji traitu From do automatycznej konwersji błędów. Actix Web wymaga implementacji zarówno Display, jak i ResponseError dla każdego typu błędu. Oba wzorce prowadzą do bezpiecznego typowo API, jednak idiom Axum jest bliższy standardowym konwencjom Rust dotyczącym propagacji błędów.
Kiedy wybrać Actix Web
Actix Web pozostaje najlepszym wyborem w następujących scenariuszach:
- Ekstremalnie wysoka przepustowość: systemy serwowania reklam programatycznych, potoki ingestii danych analitycznych, bramki handlu algorytmicznego, wszędzie tam, gdzie 10-15% dodatkowych req/s przekłada się na mierzalną wartość biznesową.
- Aplikacje oparte na WebSocket: wsparcie WebSocket w Actix Web jest sprawdzone w większej liczbie wdrożeń produkcyjnych. Wsparcie WebSocket w Axum (przez
axum::extract::ws) działa dobrze, ale ma krótszy track record produkcyjny. - Istniejące projekty Actix Web: migracja z Actix Web 3.x do 4.x przebiega płynnie. Przepisywanie stabilnej usługi produkcyjnej na Axum przynosi malejące korzyści przy rosnącym ryzyku regresji.
- Zespoły z doświadczeniem w Actix Web: zmiana frameworka wyłącznie dla lepszej ergonomii rzadko uzasadnia koszt wdrożenia nowego narzędzia i przekwalifikowania zespołu.
Kiedy wybrać Axum
Axum jest lepszym dopasowaniem w tych kontekstach:
- Nowe projekty w 2026: kompatybilność z ekosystemem Tokio (Tonic, Hyper, Tower) minimalizuje tarcia integracyjne.
- Usługi łączące HTTP i gRPC: middleware Tower działa w obu protokołach bez warstw adaptacyjnych.
- Zespoły wchodzące w ekosystem Rust: typowane ekstraktory, czytelne komunikaty błędów kompilacji i składnia
/{id}(zgodna z OpenAPI) oferują łagodniejszą krzywę nauki. - Architektury mikroserwisowe: trait
Servicez Tower umożliwia współdzielenie middleware między wieloma usługami bez duplikacji kodu.
Pytania rekrutacyjne: Actix Web i Axum w kontekście rozmów technicznych
Rozmowy kwalifikacyjne na stanowiska backendowe w Rust coraz częściej obejmują pytania dotyczące frameworków webowych. Poniższe zagadnienia pojawiają się regularnie na rozmowach na poziomie senior i lead.
P: Wyjaśnij różnicę architektoniczną między modelem runtime Actix Web a Axum.
Actix Web tworzy osobny jednowątkowy runtime Tokio dla każdego rdzenia CPU. Zadania są przypięte do wątków i nie migrują, co eliminuje narzut work-stealingu i rywalizację o linie cache. Axum korzysta z jednego wielowątkowego runtime Tokio z work-stealingiem. Model Actix Web zmniejsza contention linii cache przy dużym obciążeniu, produkując wyższą przepustowość. Model Axum upraszcza zarządzanie współdzielonym stanem, ponieważ wszystkie zadania dzielą jeden runtime.
P: Jak usunięcie #[async_trait] w Axum 0.8 wpływa na niestandardowe ekstraktory?
Axum 0.8 wykorzystuje natywne return-position impl Trait w traitach (ustabilizowane pod koniec 2023). Niestandardowe ekstraktory implementujące FromRequestParts lub FromRequest definiują teraz metody async bezpośrednio bez atrybutu #[async_trait]. Eliminuje to alokacje na stercie z Box<dyn Future> i poprawia czasy kompilacji. Istniejące ekstraktory wymagają usunięcia makra i dostosowania implementacji traitów.
P: Opisz, jak middleware Tower różni się od middleware Actix Web.
Tower definiuje generyczny trait Service<Request> niezależny od protokołu. Warstwa timeout Tower działa z HTTP (Axum), gRPC (Tonic) i dowolnym niestandardowym protokołem. Middleware Actix Web używa traitów Transform i Service specyficznych dla frameworka. Praktyczny wpływ: middleware Axum jest wielokrotnie używalny w całym ekosystemie Tower; middleware Actix Web jest specyficzny dla frameworka.
P: Jak działa walidacja zapytań SQLx w czasie kompilacji i jakie są jej kompromisy?
Makro query_as! SQLx łączy się z działającą bazą PostgreSQL podczas kompilacji. Waliduje składnię SQL, nazwy kolumn, typy i istnienie tabel. Kompromis: build wymaga dostępu do bazy danych, co komplikuje pipeline CI. SQLx dostarcza sqlx prepare do generowania metadanych zapytań offline, cachując wyniki walidacji w katalogu .sqlx commitowanym do kontroli wersji.
P: Kiedy wybór Actix Web zamiast Axum byłby technicznie uzasadniony?
Actix Web jest właściwy, gdy ciągła przepustowość jest głównym ograniczeniem: serwowanie reklam, ingestia analityki w czasie rzeczywistym lub bramki handlu algorytmicznego. Model runtime z przypiętymi wątkami eliminuje narzut work-stealingu, produkując 10-15% więcej req/s pod obciążeniem. Axum jest właściwy, gdy kompozycyjność z ekosystemem Tokio ma większe znaczenie niż marginalne zyski przepustowości, szczególnie w architekturach mikroserwisowych używających zarówno HTTP, jak i gRPC.
Kandydaci często twierdzą, że jeden framework jest uniwersalnie lepszy. Mocne odpowiedzi uznają kompromis: Actix Web optymalizuje przepustowość, Axum optymalizuje kompozycyjność ekosystemu. Właściwy wybór zależy od ograniczeń systemu, nie od osobistych preferencji.
Źródła
- TechEmpower Framework Benchmarks Round 23 (styczeń 2026): dane benchmarkowe dla testów JSON, plaintext i bazy danych
- Axum 0.8.9 Release Notes: wybór subprotokołu WebSocket, minimalna wersja Rust 1.80
- Actix Web 4.14.0 Release (czerwiec 2026): walidacja middleware tras, wsparcie IPv6 dual-stack, h1_write_buffer_size
Najważniejsze wnioski dotyczące rozwoju webowego w Rust w 2026
- Actix Web 4.14 zapewnia 10-15% wyższą przepustowość dzięki modelowi runtime z przypiętymi wątkami, co czyni go właściwym wyborem dla usług wrażliwych na latencję i wymagających wysokiej przepustowości
- Axum 0.8 oferuje lepszą ergonomię dzięki natywnym async traits, składni
/{id}i pełnej kompatybilności middleware Tower dla HTTP i gRPC - Oba frameworki używają tej samej warstwy bazodanowej (SQLx z walidacją w czasie kompilacji), więc wybór nie wpływa na wzorce dostępu do danych
- Dla nowych projektów webowych w Rust w 2026 bez ekstremalnych wymagań przepustowości, dopasowanie Axum do ekosystemu Tokio, Tonic i Tower zmniejsza długoterminowy koszt utrzymania
- Pytania rekrutacyjne na ten temat testują rozumienie modeli runtime, architektury middleware i rozumowania o kompromisach, nie preferencję frameworka
- Przygotowanie do rozmów kwalifikacyjnych z Rust wymaga zrozumienia decyzji architektonicznych obu frameworków, nie tylko składni API
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w Rust?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 22 sierpnia 2026
Tagi
Udostępnij
Powiązane artykuły

Rust i SQLx w 2026: Zapytania Sprawdzane w Czasie Kompilacji oraz Pytania Rekrutacyjne
Kompleksowy przewodnik po SQLx w Rust - zapytaniach sprawdzanych w czasie kompilacji, porównaniu z ORM-ami oraz praktycznych pytaniach na rozmowy kwalifikacyjne.

Nauka Rust w 2026: Porównanie Bootcampów i Zasobów do Samodzielnej Nauki
Kompleksowe porównanie bootcampów Rust, kursów online i darmowych materiałów edukacyjnych. Przewodnik dla programistów planujących karierę w Rust w 2026 roku.

Obsługa Błędów w Rust 2026: Result, Option, thiserror i anyhow
Kompleksowy przewodnik po obsłudze błędów w Rust wykorzystujący Result, Option, operator ? oraz biblioteki thiserror i anyhow. Poznaj najlepsze praktyki i wzorce stosowane w produkcyjnych aplikacjach.