Rust pour le Web : Actix Web vs Axum - Comparaison et Questions d'Entretien 2026
Comparaison pratique d'Actix Web 4.13 et Axum 0.8 pour le développement web en Rust en 2026. Architecture, performances, expérience développeur et questions d'entretien pour les postes backend Rust.

L'adoption des frameworks web Rust a considérablement accéléré en 2026, et deux frameworks dominent les déploiements en production : Actix Web 4.13 et Axum 0.8. Le choix entre les deux influence tout, de l'intégration des nouvelles recrues au débit en production, et la question revient fréquemment lors des entretiens backend Rust.
Actix Web 4.13 domine en débit brut (10-15 % de requêtes/seconde supplémentaires sous forte charge). Axum 0.8 offre une meilleure ergonomie grâce aux traits async natifs, la composabilité des middlewares Tower et une intégration plus étroite avec Tokio. Pour la plupart des équipes démarrant un nouveau projet en 2026, Axum constitue le choix pragmatique par défaut, sauf si des exigences extrêmes de débit dictent un autre choix.
Différences architecturales entre Actix Web et Axum
La divergence architecturale entre ces deux frameworks explique la majorité des compromis en termes de performances et d'ergonomie.
Actix Web instancie N runtimes Tokio mono-thread, un par cœur physique. Les tâches sont épinglées aux threads sans migration inter-thread. Cette approche élimine le surcoût du work-stealing et le bouncing de lignes de cache, ce qui explique l'avantage constant en débit sous charge soutenue.
Axum fonctionne sur un seul runtime Tokio multi-thread avec work-stealing. L'équipe Tokio a conçu Axum spécifiquement pour démontrer les capacités du runtime, de sorte que chaque décision de conception optimise la composabilité avec l'écosystème Tokio au sens large. Les handlers sont de simples fonctions async, et les middlewares utilisent le trait Service de 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();
}Les deux exemples compilent et s'exécutent, mais les différences sont déjà visibles. Actix Web utilise sa propre macro #[actix_web::main] et retourne std::io::Result. Axum utilise le standard #[tokio::main] et construit les routes via une structure Router. Le handler Axum retourne un extracteur typé (Json<Value>) plutôt que de construire manuellement un HttpResponse.
Benchmarks de performances : Actix Web 4.13 vs Axum 0.8
Les données de benchmark issues de TechEmpower Round 22 et de tests reproductibles communautaires montrent un schéma cohérent.
| Métrique | Actix Web 4.13 | Axum 0.8.9 | |--------|---------------|-------------| | Sérialisation JSON (req/s) | ~720 000 | ~640 000 | | Texte brut (req/s) | ~980 000 | ~870 000 | | Requête BD unique (req/s) | ~180 000 | ~170 000 | | Utilisation mémoire (hello world) | ~8 Mo | ~6 Mo | | Latence P99 (JSON) | 1,2 ms | 1,4 ms |
Actix Web maintient un avantage de débit de 10-15 % dans toutes les catégories. Axum utilise légèrement moins de mémoire grâce au runtime Tokio partagé. Pour contexte, les deux frameworks surpassent le serveur HTTP de la bibliothèque standard de Go de 2-3x et Node.js de 5-8x sur du matériel équivalent.
L'écart de performance compte pour la diffusion publicitaire, les pipelines d'analyse en temps réel et les passerelles de trading haute fréquence. Pour une API REST typique servant 10 000 requêtes/seconde, les deux frameworks dépassent largement le goulot d'étranglement, qui sera la base de données ou les appels de services externes.
Extracteurs et traitement des requêtes comparés
Les extracteurs définissent comment les frameworks analysent les requêtes entrantes. Axum 0.8 a apporté des améliorations significatives en supprimant #[async_trait] au profit des traits async natifs et en introduisant OptionalFromRequestParts pour une meilleure gestion 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
}))
}Les extracteurs d'Axum utilisent la déstructuration de tuples directement dans les paramètres de fonction. Actix Web encapsule tout dans web::Path, web::Query, etc., nécessitant des appels .into_inner(). Les deux approches sont type-safe à la compilation, mais celle d'Axum se lit plus naturellement.
Les paramètres de chemin sont passés de la syntaxe /:id à /{id} dans Axum 0.8 (via matchit 0.8). Cela s'aligne avec la syntaxe des chemins OpenAPI. L'échappement utilise les doubles accolades : {{ pour un { littéral.
Architecture des middlewares : Tower vs Actix Middleware
La composition des middlewares est l'aspect où la différence architecturale produit l'impact pratique le plus significatif.
Axum utilise les traits Service et Layer de Tower. Tout middleware compatible Tower fonctionne avec Axum, y compris les limiteurs de débit, le tracing, la compression et les couches d'authentification construites pour d'autres services Tower. Cette composabilité s'étend au-delà du HTTP ; le même middleware peut envelopper des services 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 utilise son propre système de middleware avec les traits Transform et Service (pas ceux de Tower). Les middlewares du vaste écosystème Tower nécessitent des adaptateurs ou des réécritures.
Pour les équipes déjà investies dans l'écosystème Tower via Tonic (gRPC) ou Hyper, le middleware d'Axum représente un avantage significatif. Pour les équipes construisant un service HTTP autonome, le système de middleware d'Actix Web est tout aussi capable, simplement non interchangeable.
Prêt à réussir tes entretiens Rust ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Intégration base de données avec SQLx
Les deux frameworks s'intègrent parfaitement avec SQLx, la boîte à outils SQL async-first qui valide les requêtes à la compilation. Le schéma d'intégration diffère légèrement.
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
}La couche base de données reste identique quel que soit le framework choisi. La macro query_as! de SQLx se connecte à une base de données active à la compilation et valide les noms de colonnes, les types et l'existence des tables. Une faute de frappe dans un nom de colonne produit une erreur de compilation, pas un crash en production.
Gestion des erreurs comparée
La gestion des erreurs révèle des philosophies de conception différentes. Actix Web utilise des implémentations du trait ResponseError. Axum s'appuie sur IntoResponse combiné avec le type 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 })))
}L'approche d'Axum se compose naturellement avec l'opérateur ? de Rust et le type Result. Actix Web atteint le même objectif via ResponseError, qui nécessite l'implémentation des traits Display et ResponseError. Les deux fonctionnent, mais le pattern d'Axum paraît plus idiomatique pour les développeurs Rust habitués au trait From et à la propagation d'erreurs.
Quand choisir Actix Web plutôt qu'Axum
Actix Web reste le bon choix dans des scénarios spécifiques :
- Exigences de débit maximal : Exchanges publicitaires, enchères en temps réel, pipelines d'ingestion analytique où 10-15 % de req/s supplémentaires justifient le compromis.
- Applications à forte intensité WebSocket : Le support WebSocket d'Actix Web est éprouvé en production sur davantage de déploiements. Le support WebSocket d'Axum (via
axum::extract::ws) fonctionne bien mais dispose d'un historique de production plus court. - Bases de code Actix Web existantes : La migration d'Actix Web 3.x vers 4.x est simple. Réécrire vers Axum offre des rendements décroissants pour des services stables.
- Familiarité de l'équipe : Si l'équipe connaît déjà Actix Web, changer de framework pour des gains ergonomiques rapporte rarement à court terme.
Quand choisir Axum plutôt qu'Actix Web
Axum convient mieux dans ces contextes :
- Nouveaux projets en 2026 : L'alignement avec l'écosystème Tokio (Tonic, Hyper, Tower) réduit les frictions d'intégration.
- Services mixtes gRPC et HTTP : Les middlewares Tower fonctionnent sur les deux protocoles sans couches d'adaptation.
- Équipes nouvelles en Rust : Les extracteurs typés d'Axum et les messages d'erreur à la compilation offrent une courbe d'apprentissage plus douce. La syntaxe
/{id}des chemins (alignée sur OpenAPI) est immédiatement familière. - Architectures microservices : Le trait
Servicede Tower permet la réutilisation des middlewares entre services, réduisant le code répétitif.
Questions d'entretien : Actix Web et Axum pour les postes backend Rust
Les questions d'entretien backend Rust couvrent de plus en plus les connaissances sur les frameworks web. Ces questions apparaissent dans les entretiens pour les postes d'ingénierie backend et systèmes senior.
Q : Expliquer la différence architecturale entre les modèles de runtime d'Actix Web et d'Axum.
Actix Web instancie un runtime Tokio mono-thread par cœur CPU. Les tâches sont épinglées aux threads, éliminant le surcoût du work-stealing. Axum fonctionne sur un runtime Tokio multi-thread partagé avec work-stealing. Le modèle d'Actix Web réduit la contention sur les lignes de cache sous forte charge, produisant un débit supérieur. Le modèle d'Axum simplifie la gestion de l'état partagé puisque toutes les tâches partagent un seul runtime.
Q : Comment la suppression de #[async_trait] dans Axum 0.8 affecte-t-elle les extracteurs personnalisés ?
Axum 0.8 exploite les impl Trait en position de retour natifs de Rust dans les traits (stabilisés fin 2023). Les extracteurs personnalisés implémentant FromRequestParts ou FromRequest définissent désormais les méthodes async directement sans l'attribut #[async_trait]. Cela élimine les allocations sur le tas provenant de Box<dyn Future> et améliore les temps de compilation. Les extracteurs existants nécessitent la suppression de la macro et l'ajustement des implémentations de traits.
Q : Décrire en quoi les middlewares Tower diffèrent des middlewares Actix Web.
Tower définit un trait générique Service<Request> qui est agnostique du protocole. Une couche de timeout Tower fonctionne avec HTTP (Axum), gRPC (Tonic) et tout protocole personnalisé. Les middlewares d'Actix Web utilisent les traits Transform et Service spécifiques à son framework. L'impact pratique : les middlewares Axum sont réutilisables dans l'écosystème Tower ; les middlewares Actix Web sont spécifiques au framework.
Q : Comment fonctionne la validation des requêtes à la compilation de SQLx et quels en sont les compromis ?
La macro query_as! de SQLx se connecte à une base de données PostgreSQL active pendant la compilation. Elle valide la syntaxe SQL, les noms de colonnes, les types et l'existence des tables. Le compromis : le build nécessite un accès à la base de données, ce qui complique les pipelines CI. SQLx fournit sqlx prepare pour générer des métadonnées de requête hors ligne, mettant en cache les résultats de validation dans un répertoire .sqlx commité dans le contrôle de version.
Q : Quand le choix d'Actix Web plutôt qu'Axum serait-il techniquement correct ?
Actix Web est le bon choix lorsque le débit soutenu est la contrainte principale : diffusion publicitaire, ingestion d'analyses en temps réel ou passerelles de trading haute fréquence. Le modèle de runtime à threads épinglés élimine le surcoût du work-stealing, produisant 10-15 % de req/s supplémentaires sous charge. Axum est le bon choix lorsque la composabilité avec l'écosystème Tokio compte davantage que les gains marginaux de débit, particulièrement dans les architectures microservices utilisant à la fois HTTP et gRPC.
Les candidats affirment souvent qu'un framework est universellement supérieur. Les réponses solides reconnaissent le compromis : Actix Web optimise le débit, Axum optimise la composabilité avec l'écosystème. Le bon choix dépend des contraintes du système, pas des préférences personnelles.
Conclusion
- Actix Web 4.13 délivre 10-15 % de débit supplémentaire grâce à son modèle de runtime à threads épinglés, ce qui en fait le bon choix pour les services haute performance sensibles à la latence
- Axum 0.8 offre une meilleure ergonomie avec les traits async natifs, la syntaxe
/{id}pour les chemins et une compatibilité complète avec les middlewares Tower sur HTTP et gRPC - Les deux frameworks utilisent la même couche base de données (SQLx avec validation à la compilation), donc le choix n'affecte pas les patterns d'accès aux données
- Pour les nouveaux projets web Rust en 2026 sans exigences extrêmes de débit, l'alignement d'Axum avec l'écosystème Tokio, Tonic et Tower réduit le coût de maintenance à long terme
- Les questions d'entretien sur ce sujet testent la compréhension des modèles de runtime, de l'architecture des middlewares et du raisonnement sur les compromis, pas la préférence pour un framework
- Se préparer aux entretiens Rust nécessite de comprendre les décisions architecturales des deux frameworks, pas seulement la syntaxe de leurs API
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
Tags
Partager
Articles similaires

Async/Await en Rust : Tokio, Futures et concurrence asynchrone en détail
Plongée approfondie dans async/await en Rust : le runtime Tokio, le trait Future, le lancement de tâches, la concurrence structurée et les patterns concrets pour construire des applications asynchrones performantes.

Ownership et Borrowing en Rust : le guide complet pour les entretiens techniques
Comprendre en profondeur l'ownership, le borrowing, les lifetimes et le borrow checker en Rust. Un guide essentiel pour se preparer aux entretiens techniques systemes.

Ownership et Borrowing en Rust : Guide complet
Maîtrisez le système d'ownership et borrowing de Rust. Comprendre les règles de propriété, références, lifetimes et les patterns avancés de gestion mémoire.