Rust para a Web: Actix Web vs Axum - Comparativo e Perguntas de Entrevista 2026
Comparativo prático de Actix Web 4.13 e Axum 0.8 para desenvolvimento web com Rust em 2026. Arquitetura, performance, experiência de desenvolvimento e perguntas de entrevista para vagas backend Rust.

A adoção de frameworks web em Rust acelerou significativamente em 2026, e dois frameworks dominam os deploys em produção: Actix Web 4.13 e Axum 0.8. A escolha entre eles afeta tudo, desde a integração de novos membros da equipe até a vazão em produção, e a pergunta aparece com frequência em entrevistas técnicas para posições backend em Rust.
Actix Web 4.13 lidera em vazão bruta (10-15% mais requisições/segundo sob carga pesada). Axum 0.8 oferece melhor ergonomia através de traits async nativos, composabilidade de middlewares Tower e integração mais estreita com o Tokio. Para a maioria das equipes iniciando um novo projeto em 2026, Axum é a escolha pragmática padrão, a menos que requisitos extremos de performance ditem o contrário.
Diferenças arquiteturais entre Actix Web e Axum
A divergência arquitetural entre esses dois frameworks explica a maioria dos trade-offs em performance e ergonomia.
Actix Web instancia N runtimes Tokio single-threaded, um por núcleo físico. As tasks são fixadas às threads sem migração entre elas. Isso elimina o overhead do work-stealing e o cache-line bouncing, o que explica a vantagem consistente de vazão sob carga sustentada.
Axum roda em um único runtime Tokio multi-threaded com work-stealing. A equipe do Tokio construiu o Axum especificamente para demonstrar as capacidades do runtime, então cada decisão de design otimiza a composabilidade com o ecossistema Tokio mais amplo. Os handlers são funções async simples, e os middlewares utilizam o trait Service do Tower.
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();
}Ambos os exemplos compilam e executam, mas as diferenças já são visíveis. Actix Web usa sua própria macro #[actix_web::main] e retorna std::io::Result. Axum usa o padrão #[tokio::main] e constrói rotas através de uma struct Router. O handler do Axum retorna um extractor tipado (Json<Value>) em vez de construir um HttpResponse manualmente.
Benchmarks de performance: Actix Web 4.13 vs Axum 0.8
Dados de benchmark do TechEmpower Round 22 e testes reproduzíveis da comunidade mostram um padrão consistente.
| Métrica | Actix Web 4.13 | Axum 0.8.9 | |--------|---------------|-------------| | Serialização JSON (req/s) | ~720.000 | ~640.000 | | Texto puro (req/s) | ~980.000 | ~870.000 | | Query BD única (req/s) | ~180.000 | ~170.000 | | Uso de memória (hello world) | ~8 MB | ~6 MB | | Latência P99 (JSON) | 1,2 ms | 1,4 ms |
Actix Web mantém uma vantagem de vazão de 10-15% em todas as categorias. Axum utiliza ligeiramente menos memória devido ao runtime Tokio compartilhado. Para contextualizar, ambos os frameworks superam o servidor HTTP da biblioteca padrão de Go em 2-3x e o Node.js em 5-8x no mesmo hardware.
A diferença de performance importa para ad serving, pipelines de analytics em tempo real e gateways de trading de alta frequência. Para uma API REST típica servindo 10.000 requisições/segundo, ambos os frameworks estão muito além do gargalo, que será o banco de dados ou chamadas a serviços externos.
Extractors e tratamento de requisições comparados
Extractors definem como os frameworks fazem parsing das requisições de entrada. Axum 0.8 trouxe melhorias significativas ao remover #[async_trait] em favor de traits async nativos e introduzir OptionalFromRequestParts para melhor tratamento de 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>,
per_page: Option<u32>,
}
struct AppState {
db_pool: sqlx::PgPool,
}
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);
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
}))
}Os extractors do Axum usam desestruturação de tupla diretamente nos parâmetros da função. Actix Web encapsula tudo em web::Path, web::Query, etc., exigindo chamadas .into_inner(). Ambas as abordagens são type-safe em tempo de compilação, mas a do Axum se lê de forma mais natural.
Os parâmetros de rota mudaram da sintaxe /:id para /{id} no Axum 0.8 (via matchit 0.8). Isso se alinha com a sintaxe de paths do OpenAPI. O escape usa chaves duplas: {{ para um { literal.
Arquitetura de middlewares: Tower vs Actix Middleware
A composição de middlewares é onde a diferença arquitetural produz o maior impacto prático.
Axum utiliza os traits Service e Layer do Tower. Qualquer middleware compatível com Tower funciona com Axum, incluindo rate limiters, tracing, compressão e camadas de autenticação construídas para outros serviços baseados em Tower. Essa composabilidade se estende além do HTTP; o mesmo middleware pode envolver serviços gRPC via Tonic.
use axum::{
Router, middleware,
routing::get,
extract::Request,
response::Response,
};
use tower_http::{
cors::CorsLayer,
compression::CompressionLayer,
trace::TraceLayer,
};
use std::time::Instant;
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 usa seu próprio sistema de middleware com os traits Transform e Service (não os do Tower). Middlewares do amplo ecossistema Tower requerem adaptadores ou reescrita.
Para equipes já investidas no ecossistema Tower via Tonic (gRPC) ou Hyper, o middleware do Axum é uma vantagem significativa. Para equipes construindo um serviço HTTP standalone, o sistema de middleware do Actix Web é igualmente capaz, apenas não é intercambiável.
Pronto para mandar bem nas entrevistas de Rust?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Integração com banco de dados usando SQLx
Ambos os frameworks se integram bem com SQLx, o toolkit SQL async-first que valida queries em tempo de compilação. O padrão de integração difere ligeiramente.
use sqlx::PgPool;
#[derive(sqlx::FromRow, serde::Serialize)]
struct User {
id: i64,
email: String,
created_at: chrono::NaiveDateTime,
}
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
}A camada de banco de dados permanece idêntica independentemente do framework escolhido. A macro query_as! do SQLx se conecta a um banco de dados ativo em tempo de compilação e valida nomes de colunas, tipos e existência de tabelas. Um erro de digitação no nome de uma coluna produz um erro de compilação, não uma falha em produção.
Padrões de tratamento de erros comparados
O tratamento de erros revela filosofias de design diferentes. Actix Web usa implementações do trait ResponseError. Axum se baseia em IntoResponse combinado com o tipo Result.
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
enum AppError {
NotFound(String),
DatabaseError(sqlx::Error),
ValidationError(String),
}
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()
}
}
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 })))
}A abordagem do Axum se compõe naturalmente com o operador ? do Rust e o tipo Result. Actix Web alcança o mesmo através de ResponseError, que exige a implementação dos traits Display e ResponseError. Ambos funcionam, mas o padrão do Axum é mais idiomático para desenvolvedores Rust acostumados ao trait From e à propagação de erros.
Quando escolher Actix Web em vez de Axum
Actix Web continua sendo a escolha certa em cenários específicos:
- Requisitos de vazão máxima: Ad exchanges, leilões em tempo real, pipelines de ingestão de analytics onde 10-15% mais de req/s justificam o trade-off.
- Aplicações com uso intensivo de WebSocket: O suporte WebSocket do Actix Web é battle-tested em mais deploys de produção. O suporte WebSocket do Axum (via
axum::extract::ws) funciona bem, mas tem um histórico de produção mais curto. - Codebases Actix Web existentes: A migração do Actix Web 3.x para 4.x é direta. Reescrever para Axum oferece retornos decrescentes para serviços estáveis.
- Familiaridade da equipe: Se a equipe já conhece Actix Web, trocar de framework por ganhos ergonômicos raramente compensa a curto prazo.
Quando escolher Axum em vez de Actix Web
Axum se encaixa melhor nestes contextos:
- Novos projetos em 2026: O alinhamento com o ecossistema Tokio (Tonic, Hyper, Tower) reduz a fricção de integração.
- Serviços mistos gRPC e HTTP: Middlewares Tower funcionam em ambos os protocolos sem camadas de adaptação.
- Equipes novas em Rust: Os extractors tipados do Axum e as mensagens de erro em compilação oferecem uma curva de aprendizado mais suave. A sintaxe
/{id}para rotas (alinhada com OpenAPI) é imediatamente familiar. - Arquiteturas de microsserviços: O trait
Servicedo Tower permite a reutilização de middlewares entre serviços, reduzindo boilerplate.
Perguntas de entrevista: Actix Web e Axum para vagas backend Rust
As perguntas de entrevista backend Rust cobrem cada vez mais o conhecimento sobre frameworks web. Essas perguntas aparecem em entrevistas para vagas de engenharia backend e de sistemas senior.
P: Explicar a diferença arquitetural entre os modelos de runtime do Actix Web e do Axum.
Actix Web instancia um runtime Tokio single-threaded por núcleo de CPU. As tasks são fixadas às threads, eliminando o overhead do work-stealing. Axum roda em um runtime Tokio multi-threaded compartilhado com work-stealing. O modelo do Actix Web reduz a contenção de cache-line sob carga pesada, produzindo maior vazão. O modelo do Axum simplifica o gerenciamento de estado compartilhado, já que todas as tasks compartilham um único runtime.
P: Como a remoção de #[async_trait] no Axum 0.8 afeta os extractors personalizados?
Axum 0.8 aproveita os impl Trait nativos em posição de retorno em traits do Rust (estabilizados no final de 2023). Extractors personalizados que implementam FromRequestParts ou FromRequest agora definem métodos async diretamente sem o atributo #[async_trait]. Isso elimina alocações no heap de Box<dyn Future> e melhora os tempos de compilação. Extractors existentes requerem a remoção da macro e o ajuste das implementações de traits.
P: Descrever como o middleware Tower difere do middleware do Actix Web.
Tower define um trait genérico Service<Request> que é agnóstico de protocolo. Uma camada de timeout do Tower funciona com HTTP (Axum), gRPC (Tonic) e qualquer protocolo customizado. O middleware do Actix Web usa os traits Transform e Service específicos do seu framework. O impacto prático: middleware do Axum é reutilizável no ecossistema Tower; middleware do Actix Web é específico do framework.
P: Como funciona a validação de queries em tempo de compilação do SQLx e quais são seus trade-offs?
A macro query_as! do SQLx se conecta a um banco de dados PostgreSQL ativo durante a compilação. Ela valida a sintaxe SQL, nomes de colunas, tipos e existência de tabelas. O trade-off: o build requer acesso ao banco de dados, o que complica os pipelines de CI. O SQLx fornece sqlx prepare para gerar metadados de query offline, fazendo cache dos resultados de validação em um diretório .sqlx commitado no controle de versão.
P: Quando escolher Actix Web em vez de Axum seria a decisão tecnicamente correta?
Actix Web é a escolha certa quando a vazão sustentada é a restrição principal: ad serving, ingestão de analytics em tempo real ou gateways de trading de alta frequência. O modelo de runtime com threads fixas elimina o overhead do work-stealing, produzindo 10-15% mais req/s sob carga. Axum é a escolha certa quando a composabilidade com o ecossistema Tokio importa mais que ganhos marginais de vazão, particularmente em arquiteturas de microsserviços que usam tanto HTTP quanto gRPC.
Candidatos frequentemente afirmam que um framework é universalmente superior. Respostas sólidas reconhecem o trade-off: Actix Web otimiza a vazão, Axum otimiza a composabilidade com o ecossistema. A escolha certa depende das restrições do sistema, não da preferência pessoal.
Conclusão
- Actix Web 4.13 entrega 10-15% mais de vazão através do seu modelo de runtime com threads fixas, tornando-o a escolha certa para serviços de alta performance sensíveis a latência
- Axum 0.8 proporciona melhor ergonomia com traits async nativos, sintaxe
/{id}para rotas e compatibilidade completa com middlewares Tower em HTTP e gRPC - Ambos os frameworks usam a mesma camada de banco de dados (SQLx com validação em compilação), então a escolha não afeta os padrões de acesso a dados
- Para novos projetos web em Rust em 2026 sem requisitos extremos de vazão, o alinhamento do Axum com o ecossistema Tokio, Tonic e Tower reduz o custo de manutenção a longo prazo
- As perguntas de entrevista sobre esse tema testam a compreensão de modelos de runtime, arquitetura de middlewares e raciocínio sobre trade-offs, não a preferência por um framework
- Preparar-se para entrevistas de Rust requer compreender as decisões arquiteturais de ambos os frameworks, não apenas a sintaxe de suas APIs
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

Async/Await em Rust: Tokio, Futures e concorrência assíncrona explicados em profundidade
Mergulho profundo em async/await no Rust: o runtime Tokio, a trait Future, o lançamento de tarefas, a concorrência estruturada e os padrões práticos para construir aplicações assíncronas de alto desempenho.

Ownership e Borrowing em Rust: Guia Completo para Entrevistas Tecnicas
Domine os conceitos de ownership, borrowing e lifetimes em Rust. Guia pratico com exemplos de codigo para se preparar para entrevistas tecnicas de programacao de sistemas.

Ownership e Borrowing em Rust: Guia Completo
Domine o sistema de ownership e borrowing do Rust. Regras de propriedade, referências, lifetimes e padrões avançados de gerenciamento de memória.