Rust у веб-розробці: Actix Web vs Axum - порівняння та питання на співбесіді 2026
Практичне порівняння Actix Web 4.14 та Axum 0.8 для Rust веб-розробки у 2026. Архітектура, бенчмарки TechEmpower Round 23, досвід розробника та питання на співбесіді для backend Rust позицій.

Екосистема Rust для веб-розробки досягла зрілості, достатньої для продакшн-систем будь-якого масштабу. Два фреймворки, Actix Web 4.14 та Axum 0.8, займають домінуючі позиції у цій ніші, пропонуючи розробникам гарантії безпеки пам'яті без збирача сміття та продуктивність, що наближається до рівня C. Попри спільну мову та async/await-модель, ці фреймворки реалізують принципово різні архітектурні рішення, які впливають на структуру проєкту, вибір middleware та стратегію масштабування.
Actix Web 4.14 забезпечує на 10-15% більше запитів на секунду під високим навантаженням. Axum 0.8 пропонує кращу ергономіку завдяки нативним async traits, сумісності з Tower middleware та тіснішій інтеграції з Tokio. Для більшості команд, що розпочинають новий проєкт у 2026 році, Axum є прагматичним вибором за замовчуванням, якщо екстремальні вимоги до пропускної здатності не диктують інше.
Архітектура runtime: закріплені потоки проти work-stealing
Ключова технічна відмінність між Actix Web та Axum полягає у способі організації асинхронного виконання. Actix Web використовує модель закріплених потоків (pinned-thread), де кожен worker-потік отримує власний event loop. Задачі, призначені конкретному потоку, залишаються на ньому протягом усього життєвого циклу. Така архітектура мінімізує переключення контексту та забезпечує кращу локальність кешу процесора.
Axum побудований безпосередньо на Tokio runtime з його work-stealing планувальником. Задачі розподіляються між потоками динамічно: якщо один потік завантажений, інший може "вкрасти" задачу з його черги. Цей підхід забезпечує рівномірне навантаження, але створює додатковий overhead через переміщення задач між потоками. Фреймворк був створений командою Tokio як референсний приклад використання їхнього runtime, кожен handler є звичайною async-функцією, а шар middleware базується на трейті Service з бібліотеки Tower.
Мінімальний сервер на Actix Web демонструє використання власного runtime через макрос #[actix_web::main]:
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
}Еквівалентний сервер на Axum покладається на стандартний Tokio runtime:
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();
}Відмінність у точці входу має практичне значення: #[tokio::main] у Axum дозволяє безперешкодно використовувати будь-які Tokio-сумісні бібліотеки (tonic, reqwest, tokio-tungstenite), тоді як #[actix_web::main] потребує сумісності з actix-rt. Для команд, що будують мікросервісну архітектуру з gRPC та HTTP в одному процесі, ця різниця може визначити вибір фреймворку.
Продуктивність: порівняльні бенчмарки Actix Web 4.14 та Axum 0.8
Дані з TechEmpower Round 23 (січень 2026) надають найбільш надійне порівняння. Обидва фреймворки посідають топові позиції у всіх категоріях.
| Метрика | Actix Web 4.14 | Axum 0.8.9 |
|---|---|---|
| Plaintext (req/s) | ~7 100 000 | ~6 200 000 |
| Серіалізація JSON (req/s) | ~1 200 000 | ~1 050 000 |
| Одиничний запит до БД (req/s) | ~190 000 | ~175 000 |
| Використання пам'яті (hello world) | ~8 МБ | ~6 МБ |
| Латентність P99 (JSON) | 1,1 мс | 1,3 мс |
Actix Web стабільно утримує перевагу 10-15% за пропускною здатністю у всіх категоріях. Axum споживає трохи менше пам'яті завдяки спільному Tokio runtime. Для контексту: обидва фреймворки випереджають стандартну HTTP-бібліотеку Go у 2-3 рази та Node.js у 5-8 разів на еквівалентному обладнанні.
Різниця у продуктивності має значення для показу реклами, конвеєрів аналітики в реальному часі та шлюзів високочастотного трейдингу. Для типового REST API, що обробляє 10 000 запитів на секунду, обидва фреймворки далеко за межами вузького місця, яким буде база даних або виклики зовнішніх сервісів.
Екстрактори запитів: два підходи до типобезпечного API
Екстрактори визначають, як фреймворки парсять вхідні запити. Axum 0.8 вніс значні покращення, видаливши #[async_trait] на користь нативних async traits та представивши OptionalFromRequestParts для кращої обробки Option<T>.
use axum::{
extract::{Path, Query, State, Json},
routing::get,
Router,
};
use serde::Deserialize;
use std::sync::Arc;
#[derive(Deserialize)]
struct Pagination {
page: Option<u32>, // повертає None якщо відсутній
per_page: Option<u32>,
}
// State спільний між handlers
struct AppState {
db_pool: sqlx::PgPool,
}
// Axum 0.8: синтаксис /{id} (замінив /: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);
// Запит до бази даних через 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
}))
}Екстрактори Axum використовують деструктуризацію кортежів безпосередньо в параметрах функції. Actix Web обгортає все в web::Path, web::Query тощо, вимагаючи виклики .into_inner(). Обидва підходи забезпечують типобезпеку на етапі компіляції, але синтаксис Axum читається більш природно.
Параметри шляху змінили синтаксис з /:id на /{id} в Axum 0.8 (через matchit 0.8). Це узгодження зі синтаксисом OpenAPI path. Літеральні фігурні дужки записуються подвійними: {{ для літерального {.
Middleware: екосистема Tower проти системи Actix
Вибір архітектури middleware нерідко стає визначальним фактором при виборі фреймворку. Axum повністю інтегрований з Tower, бібліотекою абстракцій для мережевих сервісів, використовуючи трейти Service та Layer. Це означає, що будь-яка middleware, написана для екосистеми Tower (rate limiters, tracing, compression, authentication), працює з Axum без модифікацій. Більше того, ця композиційність виходить за межі HTTP: та сама middleware може обгортати gRPC-сервіси через Tonic.
use axum::{
Router, middleware,
routing::get,
extract::Request,
response::Response,
};
use tower_http::{
cors::CorsLayer,
compression::CompressionLayer,
trace::TraceLayer,
};
use std::time::Instant;
// Кастомна middleware як звичайна async-функція
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 має власну middleware-систему з трейтами Transform та Service (не Tower). Middleware з Tower-екосистеми потребує адаптерів або переписування.
Для команд, вже інвестованих в Tower-екосистему через Tonic (gRPC) або Hyper, middleware Axum є значною перевагою. Для команд, що будують ізольований HTTP-сервіс, middleware-система Actix Web є настільки ж потужною, просто не взаємозамінною.
Actix Web 4.14 ввів валідацію middleware маршрутів, яка викликає panic, якщо handlers додаються після обгортання middleware, ловлячи помилки конфігурації при запуску замість runtime.
Готовий до співбесід з Rust?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
Асинхронний доступ до бази даних через SQLx
SQLx є стандартом де-факто для асинхронної роботи з базами даних у Rust. Унікальна особливість крейту, валідація SQL-запитів під час компіляції. Шар бази даних залишається ідентичним незалежно від вибору фреймворку.
use sqlx::PgPool;
// Цей struct працює ідентично з Actix Web та Axum
#[derive(sqlx::FromRow, serde::Serialize)]
struct User {
id: i64,
email: String,
created_at: chrono::NaiveDateTime,
}
// sqlx::query_as! валідує проти живої БД під час компіляції
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
}Макрос query_as! підключається до живої бази даних під час компіляції та валідує імена колонок, типи та існування таблиць. Помилка в імені колонки викликає помилку компіляції, а не збій у продакшені. Компроміс: збірка потребує доступу до бази даних, що ускладнює CI-пайплайни. SQLx надає sqlx prepare для генерації офлайн-метаданих запитів, кешуючи результати валідації в директорії .sqlx, що комітиться у version control.
Обробка помилок: IntoResponse проти ResponseError
Обробка помилок виявляє різні філософії проєктування. Actix Web використовує реалізації трейту ResponseError. Axum покладається на IntoResponse у поєднанні з типом Result.
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
// Визначення помилок рівня застосунку
enum AppError {
NotFound(String),
DatabaseError(sqlx::Error),
ValidationError(String),
}
// Перетворення помилок на HTTP-відповіді
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 повертають 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 })))
}Підхід Axum природно поєднується з оператором ? та типом Result Rust. Actix Web досягає того ж через ResponseError, який вимагає реалізації трейтів Display та ResponseError. Обидва працюють, але патерн Axum відчувається більш ідіоматичним для Rust-розробників.
Коли варто обрати Actix Web
Actix Web залишається правильним вибором у конкретних сценаріях:
- Максимальні вимоги до пропускної здатності: показ реклами, real-time bidding, конвеєри аналітики, де 10-15% більше req/s виправдовує компроміс.
- WebSocket-орієнтовані застосунки: підтримка WebSocket в Actix Web перевірена більшою кількістю продакшн-деплоїв. Підтримка WebSocket в Axum (через
axum::extract::ws) працює добре, але має коротший продакшн track record. - Існуючі кодові бази Actix Web: міграція з Actix Web 3.x до 4.x проходить гладко. Переписування стабільних сервісів на Axum дає спадаючу віддачу.
- Досвід команди: якщо команда вже знає Actix Web, зміна фреймворків заради ергономічних покращень рідко окупається в короткостроковій перспективі.
Коли варто обрати Axum
Axum краще підходить у цих контекстах:
- Нові проєкти у 2026: узгодженість з екосистемою Tokio (Tonic, Hyper, Tower) зменшує тертя інтеграції.
- Змішані gRPC та HTTP сервіси: Tower middleware працює на обох протоколах без шарів адаптації.
- Команди, нові в Rust: типо-орієнтовані екстрактори Axum та повідомлення про помилки компіляції забезпечують м'якшу криву навчання. Синтаксис
/{id}(узгоджений з OpenAPI) одразу впізнається. - Мікросервісні архітектури: трейт
ServiceTower дозволяє перевикористання middleware між сервісами, зменшуючи boilerplate.
Питання та відповіді для технічних співбесід
Backend питання на співбесідах з Rust все частіше охоплюють знання веб-фреймворків. Ці питання з'являються на senior backend та systems engineering співбесідах.
П: Поясніть архітектурну різницю між моделями runtime Actix Web та Axum.
Actix Web створює один однопоточний Tokio runtime для кожного ядра CPU. Задачі закріплені за потоками і не мігрують, усуваючи overhead work-stealing. Axum працює на спільному багатопоточному Tokio runtime з work-stealing. Модель Actix Web зменшує contention cache-line під навантаженням, виробляючи вищу пропускну здатність. Модель Axum спрощує управління спільним станом, оскільки всі задачі ділять один runtime.
П: Як видалення #[async_trait] в Axum 0.8 впливає на кастомні екстрактори?
Axum 0.8 використовує нативні return-position impl Trait в трейтах (стабілізовано наприкінці 2023). Кастомні екстрактори, що реалізують FromRequestParts або FromRequest, тепер визначають async-методи безпосередньо без атрибуту #[async_trait]. Це усуває heap-алокації з Box<dyn Future> та покращує час компіляції. Існуючі екстрактори потребують видалення макросу та коригування реалізацій трейтів.
П: Опишіть, чим Tower middleware відрізняється від middleware Actix Web.
Tower визначає узагальнений трейт Service<Request>, що є протокол-агностичним. Timeout layer Tower працює з HTTP (Axum), gRPC (Tonic) та будь-яким кастомним протоколом. Middleware Actix Web використовує трейти Transform та Service, специфічні для фреймворку. Практичний вплив: middleware Axum перевикористовується в екосистемі Tower; middleware Actix Web є фреймворк-специфічною.
П: Як працює валідація запитів SQLx під час компіляції і які компроміси?
Макрос query_as! SQLx підключається до живої бази PostgreSQL під час компіляції. Він валідує SQL-синтаксис, імена колонок, типи та існування таблиць. Компроміс: build потребує доступу до бази даних, що ускладнює CI-пайплайни. SQLx надає sqlx prepare для генерації офлайн-метаданих запитів, кешуючи результати валідації в директорії .sqlx, що комітиться у version control.
П: Коли вибір Actix Web замість Axum був би технічно правильним рішенням?
Actix Web правильний, коли стабільна пропускна здатність є основним обмеженням: показ реклами, real-time analytics ingestion або шлюзи високочастотного трейдингу. Модель runtime з закріпленими потоками усуває overhead work-stealing, виробляючи на 10-15% більше req/s під навантаженням. Axum правильний, коли композиційність з екосистемою Tokio важливіша за маргінальні виграші в пропускній здатності, особливо в мікросервісних архітектурах, що використовують і HTTP, і gRPC.
Кандидати часто стверджують, що один фреймворк універсально кращий. Сильні відповіді визнають компроміс: Actix Web оптимізує для пропускної здатності, Axum оптимізує для композиційності екосистеми. Правильний вибір залежить від обмежень системи, а не особистих уподобань.
Джерела
- TechEmpower Framework Benchmarks Round 23 (січень 2026): дані бенчмарків для тестів JSON, plaintext та бази даних
- Axum 0.8.9 Release Notes: вибір WebSocket subprotocol, мінімальна версія Rust 1.80
- Actix Web 4.14.0 Release (червень 2026): валідація middleware маршрутів, підтримка IPv6 dual-stack, h1_write_buffer_size
Ключові висновки для Rust веб-розробки у 2026
- Actix Web 4.14 забезпечує на 10-15% вищу пропускну здатність завдяки моделі runtime з закріпленими потоками, що робить його правильним вибором для сервісів, чутливих до латентності та з високою пропускною здатністю
- Axum 0.8 надає кращу ергономіку з нативними async traits, синтаксисом
/{id}та повною сумісністю Tower middleware для HTTP та gRPC - Обидва фреймворки використовують однаковий шар бази даних (SQLx з валідацією під час компіляції), тому вибір не впливає на патерни доступу до даних
- Для нових Rust веб-проєктів у 2026 без екстремальних вимог до пропускної здатності, узгодженість Axum з екосистемою Tokio, Tonic та Tower зменшує довгострокову вартість обслуговування
- Питання на співбесідах з цієї теми тестують розуміння моделей runtime, архітектури middleware та міркування про компроміси, а не уподобання фреймворку
- Підготовка до співбесід з Rust вимагає розуміння архітектурних рішень обох фреймворків, а не лише синтаксису API
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Чи знайдеш ти помилку в Rust?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 22 серпня 2026 р.
Теги
Поділитися
Пов'язані статті

Rust та SQLx у 2026: Запити з Перевіркою під час Компіляції та Питання на Співбесідах
Повний посібник з використання SQLx у Rust - перевірка запитів під час компіляції, порівняння з ORM та практичні питання для технічних співбесід.

Вивчення Rust у 2026: Порівняння Буткемпів та Ресурсів для Самонавчання
Комплексне порівняння буткемпів Rust, онлайн-курсів та безкоштовних навчальних матеріалів. Посібник для розробників, які планують кар'єру в Rust у 2026 році.

Обробка Помилок у Rust 2026: Result, Option, thiserror та anyhow
Повний посібник з обробки помилок у Rust з використанням Result, Option, оператора ? та бібліотек thiserror і anyhow. Найкращі практики та патерни для продакшн застосунків.