Rust у веб-розробці: Actix Web vs Axum - порівняння та питання на співбесіді 2026

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

Порівняння Rust Actix Web та Axum

Екосистема 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]:

actix_hello.rs - Actix Web minimal serverrust
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:

axum_hello.rs - Axum minimal serverrust
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.14Axum 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>.

axum_extractors.rs - Axum 0.8 extractor patternrust
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
    }))
}
actix_extractors.rs - Actix Web extractor patternrust
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 читається більш природно.

Зміна в Axum 0.8

Параметри шляху змінили синтаксис з /: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.

axum_middleware.rs - Tower middleware compositionrust
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-запитів під час компіляції. Шар бази даних залишається ідентичним незалежно від вибору фреймворку.

shared_db.rs - SQLx with compile-time query validationrust
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.

axum_errors.rs - Axum error handling with IntoResponserust
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) одразу впізнається.
  • Мікросервісні архітектури: трейт Service Tower дозволяє перевикористання 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 оптимізує для композиційності екосистеми. Правильний вибір залежить від обмежень системи, а не особистих уподобань.

Джерела

Ключові висновки для 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

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 22 серпня 2026 р.

Теги

#rust
#actix-web
#axum
#web-framework
#backend
#comparison

Поділитися

Пов'язані статті