Tech · Backend · Rust · Octobre 2026

Quel framework Rust choisir ? Quand aucun ne s’impose

Vous démarrez un backend en Rust, et chacun vous conseille un framework différent. En Java ou en Python, la question serait presque réglée d’avance. En Rust, elle reste ouverte. Voici de quoi la trancher selon votre situation, et pourquoi la décision qui compte le plus se prend sous le framework.

Écrit pour
  • les tech leads
  • les devs

Demandez à une équipe Java avec quoi elle démarre un nouveau backend : la réponse tient en deux mots, Spring Boot. Dans la plupart des langages, un grand nom s’est imposé de la même façon : Django ou FastAPI en Python, ASP.NET Core en C#, Rails en Ruby. Des alternatives sérieuses existent partout, mais elles se définissent par rapport à lui.

Posez la même question à des développeurs Rust, et vous aurez six réponses : axum, Actix Web, Rocket, Poem, Salvo, Loco. Plusieurs sont excellents et tournent en production ; aucun n’a gagné au point de rendre la question inutile. Pour comprendre cette diversité, et surtout pour choisir, il est utile de revenir d’abord aux bases : ce que fait un backend, et ce qu’un framework lui apporte.

Un fichier nous servira de fil rouge : le Cargo.toml, où un projet Rust déclare ses dépendances. Il est vide au départ ; chaque partie lui ajoutera une ligne ou une décision, et à la fin, vous saurez lire le vôtre comme une décision d’architecture.

Cet article décrit l’écosystème tel qu’il est fin octobre 2026 : versions, dates et téléchargements ont été relevés sur crates.io le 28 octobre. Les grandes lignes tiendront longtemps ; les numéros de version, eux, bougent vite. Je mettrai l’article à jour quand l’essentiel changera.
Partie 1 · [backend]

Ce que fait un backend, et ce qu’un framework lui apporte

Une application backend a une mission principale : recevoir des requêtes, les traiter, et répondre. Ces requêtes arrivent presque toujours en HTTP, le protocole du web : c’est le cas d’une API REST, appelée par un navigateur ou une application mobile.

Prenons un exemple qui nous suivra tout l’article : mon-api, un petit service fictif écrit en Rust. Il sert des articles. Un client lui demande l’article n° 42. Suivons sa requête, de son arrivée à la réponse :

Le clientun navigateur, une application mobile : il envoie une requête, puis attend la réponse.
la requêteGET /articles/42 la réponse200 OKet l’article en JSON
Le backendle programme mon-api, écrit en Rust : le langage est le cadre de tout le reste
  1. 1Runtime asynchronefait avancer des milliers de requêtes à la fois sur quelques threadsfondations : tokio
  2. 2Serveur HTTPreçoit la connexion et décode la requête HTTPfondations : hyper
  3. 3Middlewarestraitements communs à toutes les requêtes : journal, authentification…fondations : tower
  4. 4Routeur et extracteurschoisit la fonction qui répond, et tire de la requête ce qu’elle attend : l’identifiant, le JSONframework
  5. 5Handlerla fonction qui répond : appelle le métier, rend la réponsevotre code
  6. 6Métierles règles de l’application, la base de donnéesvotre code
1 à 4 : la plomberie, la même pour toutes les applications5 et 6 : ce qui fait votre application
votre codeframeworkfondations, des crates partagéesbibliothèque standard : aucune étapelangage, le cadre
La requête traverse les six étapes, puis la réponse repart vers le client. Les couleurs disent qui fournit chaque étape dans un backend axum, le cas le plus courant ; ce sont les mêmes dans tous les schémas de l’article. Chez d’autres frameworks, le framework fournit aussi l’étape 3, voire la 2 : c’est le critère qui compte le plus, on y reviendra.

Les étapes 1 à 4 se ressemblent d’une application à l’autre, et personne n’a envie de les réécrire à chaque fois. C’est le rôle du framework :

Définition

Un framework est un ensemble d’outils déjà écrits qui prend en charge la plomberie d’une application (ici, les étapes 1 à 4) et impose une façon d’organiser le reste. Il ne change pas le langage : c’est du code écrit dans ce langage, que vous ajoutez à votre projet.

Dans le schéma, pourtant, une seule étape porte la couleur du framework. Les trois premières sont fournies par des fondations : des bibliothèques partagées par presque tous les frameworks Rust, et que la bibliothèque standard, elle, ne fournit pas. Comparons avec Java, où c’est le framework qui fait tout :

votre codeframeworkfondationsbibliothèque standardlangage
Mêmes couches, mêmes couleurs : seule la taille change. En Java, le framework assure toute la plomberie, serveur HTTP compris. En Rust, la couche épaisse est en dessous : des fondations communes, sur lesquelles le framework n’est plus qu’une couche fine. Les numéros renvoient aux étapes du trajet.

Où vivent ces couches : les crates

Définition

En Rust, une bibliothèque s’appelle une crate, et se publie sur crates.io. Le Cargo.toml est le fichier qui déclare celles dont votre projet dépend. Le framework y est une ligne, à côté des fondations : c’est le fil rouge de cet article.

Ce qu’on peut confier au framework

Tous les frameworks n’en font pas autant. Les plus minimalistes s’arrêtent au routage et à la lecture des requêtes ; les plus intégrés décident aussi de la base de données, des tâches de fond, voire de l’interface. Chaque décision qu’il prend est une décision que vous n’aurez pas à prendre, et une décision que vous ne pourrez plus prendre autrement.

Ces deux idées, ce que le framework remplace dans les fondations et ce qu’on lui confie au-dessus, deviendront les deux premiers critères de la partie 3.

$ cargo new mon-api # [dependencies] : vide. Rust ne vous impose rien, pas même un serveur HTTP.
[backend] · ce qu’on retient

Un backend, ce sont six étapes, et un framework en prend en charge la plomberie. En Rust, des fondations partagées en font l’essentiel, et la bibliothèque standard rien du tout. Ce n’est pas un hasard : c’est une philosophie, et une histoire.

[history] · Philosophie et histoire
Partie 2 · [history]

Philosophie et histoire de Rust

L’absence d’un grand framework en Rust n’est ni un retard ni un accident. Elle découle de choix faits par le langage, et d’une histoire mouvementée. Les deux expliquent pourquoi, dans le trajet de la partie 1, ce sont les fondations qui font l’essentiel du travail.

Une bibliothèque standard qui s’arrête avant le réseau

La partie 1 l’a montré : la bibliothèque standard de Rust ne fournit aucune étape du trajet. Comparez-la à celle de Go, l’autre langage où aucun framework ne s’est imposé :

Sans aucune dépendance…GoRust
Ouvrir une connexion TCPouioui, bloquante
Exécuter du code concurrentoui, les goroutinesà moitié : async, sans moteur
Être un serveur HTTPoui, net/httpnon
Lire et écrire du JSONouinon
Chiffrer avec TLSouinon
En Go, un serveur web sort de la boîte, et le framework est facultatif. En Rust, même le serveur le plus simple commence par des dépendances.

C’est un choix assumé : ce qui entre dans la bibliothèque standard de Rust y reste pour toujours, avec une promesse de compatibilité sans date de fin. Or HTTP bouge (HTTP/2, puis HTTP/3), et la bonne façon d’exécuter du code asynchrone dépend de la machine, du serveur au microcontrôleur. Rust a donc laissé tout cela aux crates. Et dès qu’il y a dépendance, il y a choix.

L’asynchrone est arrivé tard, et son moteur est resté dehors

Les mots-clés async et await ne sont stables en Rust que depuis novembre 2019 (version 1.39). Le langage définit ce qu’est une tâche asynchrone, mais pas qui l’exécute : il faut un runtime, et pendant des années, deux se sont disputé la place, tokio et async-std. Une bibliothèque écrite pour l’un ne marchait pas toujours avec l’autre, et chaque framework devait parier.

tokio a gagné : version 1.0 en décembre 2020, puis adoption générale. En février 2025, async-std a été officiellement arrêté. Tide, le framework web bâti dessus, n’a plus publié de version depuis décembre 2021 : retenez ce nom, il reviendra comme une leçon.

Frise de 2015 à 2026. En haut, les frameworks : Rocket 0.1 fin 2016, Actix Web en 2017, warp en 2018, la crise d’Actix en janvier 2020, axum 0.1 en 2021, Actix Web 4 en 2022, Rocket 0.5 fin 2023, axum 0.8 en janvier 2025, puis en 2026 Loco 1.0, Salvo 1.0 et Rocket 0.5.2. En bas, les fondations : Rust 1.0 en 2015, async/await stable fin 2019, tokio 1.0 fin 2020, hyper 1.0 fin 2023, l’arrêt d’async-std début 2025, et tonic qui rejoint le projet gRPC en 2026. FRAMEWORKS FONDATIONS 2015201620172018 2019202020212022 2023202420252026 Rocket 0.1 Actix Web warp axum 0.1 Actix Web 4 Rocket 0.5 axum 0.8 Loco 1.0, Salvo 1.0 Rocket 0.5.2 la crise d’Actix Rust 1.0 async/await stable tokio 1.0 hyper 1.0 async-std arrêté tonic rejoint gRPC
Onze ans d’histoire, d’après les dates de publication sur crates.io et les annonces des projets.

Les fondations ne sont stables que depuis peu : hyper, la bibliothèque HTTP de presque tout le monde, n’a publié sa 1.0 qu’en novembre 2023. Spring Boot a été bâti sur une plateforme Java vieille de près de vingt ans ; les frameworks Rust, pendant que le sol bougeait encore. Aucun n’a eu le temps d’écraser les autres.

Pas de réflexion, donc pas de magie à l’exécution

La puissance de Spring repose en grande partie sur la réflexion : la capacité d’un programme Java à s’inspecter pendant qu’il tourne. Au démarrage, Spring parcourt vos classes, lit leurs annotations et injecte les dépendances là où vous les avez demandées. On annote, et ça marche.

Rust n’a pas de réflexion à l’exécution : un framework ne peut pas « découvrir » votre code au démarrage. Tout se résout à la compilation, par les types, les traits et les macros. Comparez :

Java, Spring Boot
@RestController
class ArticleController {
    // injecté au démarrage, par réflexion
    @Autowired ArticleRepository repo;

    @GetMapping("/articles/{id}")
    Article get(@PathVariable long id) {
        return repo.findById(id).orElseThrow();
    }
}
Rust, axum
// une fonction ordinaire : ses paramètres disent
// ce qu’elle attend, et le compilateur le vérifie
async fn article(
    State(db): State<Db>,
    Path(id): Path<u64>,
) -> Result<Json<Article>, StatusCode> {
    db.find(id).await.map(Json).ok_or(StatusCode::NOT_FOUND)
}
// … et c’est vous qui passez l’état au routeur :
// Router::new().route(…).with_state(db)

Côté Spring, le framework fait un travail énorme et invisible. Côté Rust, pas de conteneur d’injection : l’état est passé explicitement, et la signature de la fonction est le contrat. Si un paramètre ne peut pas être extrait de la requête, le code ne compile pas. D’où l’idée la plus importante de cette partie :

Ailleurs, le framework s’en chargeEn Rust, un type le fait
L’injection de dépendancesun paramètre : State<Db>
La lecture et la validation des donnéesun type qui refuse les valeurs invalides : Json<NouvelArticle>
La gestion des erreursun type de retour : Result
La découverte des routes, au démarragedes routes déclarées dans le code, vérifiées à la compilation

En Rust, le système de types fait une partie du travail qu’un framework fait ailleurs. Le framework n’a plus besoin d’être une plateforme : il peut se contenter d’être un routeur bien typé.

Une culture de la composition

L’écosystème Rust a pris très tôt l’habitude des petites crates qui font une seule chose, très bien : serde pour sérialiser, tracing pour les journaux et les traces, sqlx pour la base de données. Aucune n’appartient à un framework, et c’est voulu. Faiblesse, parce qu’il faut assembler soi-même ; force rare, parce qu’on remplace une brique sans changer de maison. Le reste de l’article consiste à savoir laquelle des deux compte le plus pour vous.

Ce que cette histoire a produit : des fondations communes

Résultat : dans presque tout backend Rust en production, on retrouve les mêmes étages, ceux des étapes 1 à 4 du trajet. Le framework se pose dessus, et ce n’est pas lui le plus épais. Le schéma ci-dessous les montre pour chaque framework, avec les couleurs de la partie 1 :

Les étages d’un backend Rust, du bas vers le haut, avec les numéros du trajet d’une requête. Étape 1, le runtime : tokio, une fondation commune à tous. Étape 2, le protocole HTTP : hyper, une fondation commune, pour axum, Poem, Salvo et Rocket ; Actix Web a son propre moteur, actix-http. Étape 3, le modèle de middleware : tower, une fondation commune, pour axum et tonic ; Poem et Salvo ont leurs middlewares maison, Rocket ses fairings, Actix Web actix-service, tous propres au framework. Étape 4, les frameworks eux-mêmes. Au-dessus d’axum seulement, les méta-frameworks : Loco, Cot, Leptos, Dioxus. Une bande transverse rappelle que serde, tracing, sqlx et reqwest servent quel que soit le framework. Au-dessusméta-frameworks 4 Frameworkrouteur, extracteurs 3 Middlewarece qui enveloppe 2 HTTPle protocole 1 Runtimele moteur Partoutbriques transverses Loco · Cot · Leptos · Dioxus bâtis sur axum axum et tonic, son voisin gRPC Poem · Salvo plus intégrés Rocket macros et conventions Actix Web un monde complet tower · tower-http un contrat partagé maison passerelle tower chez Poem fairings le modèle de Rocket actix-service son propre trait Service hyper HTTP/1 et HTTP/2, côté serveur comme côté client (reqwest) actix-http son propre moteur tokio pour tous ; Actix Web l’utilise à travers actix-rt, avec un runtime par cœur serde · tracing · sqlx, SeaORM, Diesel · reqwest indépendants du framework : ils vous suivent si vous en changez
fondations communespropre au frameworkbriques transverses
Simplifié. Les numéros sont ceux du trajet de la partie 1. Seule la colonne d’axum repose entièrement sur les fondations communes ; plus une colonne prend la couleur du framework, plus il les a remplacées par ses propres briques. C’est ce qu’on mesurera dans la partie 3.
tokiole runtime

Le moteur, et le standard de fait

tokio exécute vos tâches asynchrones : il répartit le travail sur tous les cœurs, gère les entrées-sorties non bloquantes, les minuteries, les canaux. Tous les frameworks de cet article tournent dessus, Actix Web compris, qui l’utilise à sa façon : un petit runtime indépendant par cœur.

Les alternatives visent des cas précis (smol pour le minimalisme, glommio ou monoio pour io_uring sous Linux, embassy pour les microcontrôleurs). Si vous construisez un backend web et que vous hésitez, vous n’êtes pas dans l’un de ces cas.

hyperle protocole

HTTP, fait une fois, bien

hyper implémente HTTP/1 et HTTP/2, côté serveur comme côté client. On l’utilise rarement directement : axum, tonic, Rocket, Poem, Salvo, warp et reqwest, le client HTTP le plus répandu, s’appuient dessus. Sa 1.0, en novembre 2023, a donné pour la première fois une API stable à la pile HTTP de Rust. Seul Actix Web a son propre moteur, actix-http.

towerle contrat

Le contrat qui change tout

C’est l’étage le moins connu, et pourtant le plus important pour votre décision. tower ne fait presque rien par lui-même : il définit un contrat, le trait Service. Un service, c’est quelque chose qui reçoit une requête et qui rendra, plus tard, une réponse ou une erreur.

Le cœur de tower (simplifié)
pub trait Service<Request> {
    type Response;
    type Error;
    type Future: Future<Output = Result<Self::Response, Self::Error>>;

    // « Es-tu prêt à recevoir une requête ? » : la contre-pression fait partie du contrat.
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;

    // Traiter la requête : la réponse arrivera plus tard.
    fn call(&mut self, req: Request) -> Self::Future;
}

La conséquence est décisive : dans le monde tower, un middleware est simplement un service qui en enveloppe un autre (une Layer). Écrite une fois, une couche enveloppe n’importe quel service, quel que soit le framework :

Deux services enveloppés par les mêmes couches. À gauche, un routeur axum ; à droite, un serveur gRPC tonic. Autour de chacun, de l’extérieur vers l’intérieur : TraceLayer, qui trace chaque requête, CorsLayer, qui gère les en-têtes CORS, et CompressionLayer, qui compresse les réponses. Ce sont les mêmes couches, écrites une seule fois. TraceLayerune trace par requête CorsLayerles en-têtes CORS CompressionLayergzip, brotli Routeur axum vos handlers REST TraceLayerla même CorsLayerla même CompressionLayerla même Serveur tonic vos services gRPC Une requête traverse les couches de l’extérieur vers l’intérieur, et la réponse en ressort dans l’autre sens.
Les mêmes couches, écrites une fois, autour de deux services différents. C’est ce contrat partagé qu’on appellera la « famille tower ». La crate tower-http en fournit toute une collection, prête à l’emploi : en code, cela donne ceci.
Des couches tower-http autour d’un routeur axum
let app = Router::new()
    .route("/articles/{id}", get(article))
    .layer(CompressionLayer::new())      // tower-http : gzip, brotli…
    .layer(CorsLayer::permissive())      // tower-http : en-têtes CORS
    .layer(TraceLayer::new_for_http());  // tower-http : une trace par requête, via tracing
// Les mêmes couches peuvent envelopper un serveur gRPC tonic.

Voilà le secret de l’ascension d’axum, publié en 2021 seulement : il est arrivé sur un écosystème qui existait déjà, et qui n’attendait qu’un routeur. axum n’est pas un framework qui a créé son écosystème : c’est un écosystème qui a trouvé son framework.

transversepartout

Ce qui vous suit, quel que soit le framework

serde pour le JSON, tracing pour les journaux et les traces (et OpenTelemetry), sqlx, SeaORM ou Diesel pour la base de données, reqwest pour appeler d’autres services, thiserror et anyhow pour les erreurs. Aucune ne dépend du framework. Dans un vrai projet, la ligne du framework est souvent l’une des plus courtes du Cargo.toml.

Deux familles, et quelques indépendants

Selon les étages que chaque framework réutilise ou réinvente, le paysage se range en deux familles : la famille tower, qui partage ses middlewares avec tout l’écosystème, et les indépendants, qui ont chacun leur propre modèle, parfois très réussi, mais à part.

FrameworkHTTPMiddlewareStyle d’écritureMacros
axumhypertowerFonctions et extracteursPresque aucune
Loco, Cot, Leptos, Dioxushyper (via axum)towerCelui du méta-frameworkVariable
Actix Webactix-httpactix-serviceFonctions et extracteursPour les routes (facultatives)
RockethyperFairingsMacros d’attributPartout
PoemhyperMaison (passerelle tower)Fonctions et extracteurs#[handler]
SalvohyperMaisonTout est handler#[handler]
warphyperFiltresCombinateursAucune

Ce tableau n’a l’air de rien. Il contient pourtant le critère le plus structurant de cet article, et le moins discuté. On y revient dans la partie 3.

Ce que ça change pour vous

Le standard est plus bas dans la pile

Il n’y a pas de framework standard, mais il y a des fondations standards : tokio, hyper, serde, tracing. C’est là que l’écosystème s’est unifié, et c’est là que se joue la longévité de votre code.

Un mauvais choix coûte moins cher qu’ailleurs

Vos types métier, vos structures serde, vos requêtes sqlx ne dépendent pas du framework. Changer de framework, c’est réécrire les routes, les handlers, parfois les middlewares, pas l’application. Quitter Spring, c’est souvent tout réécrire.

Mais l’assemblage est pour vous

La configuration, l’authentification, l’ORM, la documentation de l’API : un framework minimal vous laisse choisir chacun. C’est la vraie question que pose un framework Rust : quelle part de l’assemblage voulez-vous faire vous-même ?

$ cargo add tokio --features full $ cargo add serde --features derive $ cargo add tracing tracing-subscriber # quatre lignes dans [dependencies], et toujours pas de framework
[history] · ce qu’on retient

Rust n’a pas de Spring parce qu’il n’en a pas besoin de la même façon : les types font une partie du travail, et l’écosystème s’est unifié plus bas, dans des fondations communes. Reste à savoir comment comparer les frameworks posés dessus.

[criteria] · Les critères
Partie 3 · [criteria]

Les critères pour choisir un framework Rust

Pour comparer des outils aussi différents, il faut une grille. Huit critères la composent. Les deux premiers découlent directement de la partie 2 et dessinent la carte des candidats ; les six autres la précisent. Et celui qu’on cite toujours en premier, la vitesse, n’en fait presque pas partie.

  1. 1La distance aux fondations communes
  2. 2Le niveau d’intégration
  3. 3La maintenance
  4. 4La stabilité de l’API
  5. 5Le modèle d’exécution
  6. 6Les erreurs de compilation
  7. 7Le contrat d’API
  8. 8L’équipe
  9. ✕La vitesse brute

1La distance aux fondations communes, le critère par excellence

L’écosystème Rust s’est unifié sous le framework, dans tokio, hyper et tower. Un framework Rust se juge donc d’abord à ce qu’il en garde :

Définition

La distance d’un framework aux fondations communes, c’est tout ce qu’il remplace par ses propres briques : son modèle de middleware (l’étape 3 du trajet), parfois son serveur HTTP (l’étape 2).

Cette distance a deux prix :

Conséquence 1 · le coût de sortie

Ce qu’il faudrait réécrire le jour où l’on quitte le framework, pour un autre outil ou pour les fondations seules.

Conséquence 2 · l’écosystème perdu

Tout ce qui parle tower et dont on n’hérite plus : tower-http, tonic sur le même port, lambda_http, les méta-frameworks.

Le jour du départ arrive plus souvent qu’on ne le croit. C’est ce qui est arrivé à Tide :

L’outil n’est plus maintenu Une version cassante arrive Vos besoins changent : gRPC, serverless, contrat publié

Comment la mesurer : le modèle de middleware

La distance se lit en une ligne, à l’étape 3 du trajet : ce qu’il faut implémenter pour écrire un middleware. Le contrat commun de tower, ou un modèle maison ? Voici la réponse de chaque framework :

impl tower::Layer<S>axumLocotonic
impl poem::Middleware<E>Poem
#[handler] async fn(…, ctrl: &mut FlowCtrl)Salvo
impl rocket::fairing::FairingRocket
impl actix_web::dev::Transform<S, ServiceRequest>Actix Web

La première ligne est le contrat de tout l’écosystème tower : une couche écrite pour l’un sert à tous. Les quatre autres sont des modèles propres à leur framework, souvent bien faits, mais qui ne se branchent que chez lui. Actix Web remplace en plus le serveur HTTP.

Voici ce que cela change pour deux projets identiques, l’un écrit avec axum, l’autre avec Actix Web, au moment de les quitter :

à réécrire pour partirsurvit
Dans les deux cas, routes et handlers sont à réécrire : chaque framework Rust a ses extracteurs. La différence est dessous. Avec axum, vos middlewares sont des couches tower : elles vous suivent chez n’importe quel framework de la famille, et servent aussi tonic. Avec Actix Web, ils partent avec lui.
Plus un framework remplace les fondations communes par ses propres briques, plus il en fait à sa façon, moins il hérite de l’écosystème, et plus la sortie coûte.

Ce n’est pas un défaut : c’est un échange. Mais il se décide le premier jour, pas le jour du départ.

2Le niveau d’intégration, le second axe

Rust vous livre le backend en kit. Certains frameworks vous livrent les pièces et la notice ; d’autres, le meuble déjà monté. Ce sont les huit décisions de la partie 1, posées sur une ligne :

Un éventail, du kit au meuble monté. À gauche, hyper seul : vous décidez de tout. Puis axum, qui fournit le routage et les extracteurs ; Poem et Salvo, qui ajoutent OpenAPI, TLS et d’autres fonctions ; Rocket, avec ses conventions et ses macros ; Loco et Cot, avec un ORM, des tâches de fond, des e-mails et des générateurs ; et à droite, Leptos et Dioxus, qui fournissent aussi l’interface. Plus on va vers la droite, plus le framework décide pour vous. ce que vous assemblez vous-même ce que le framework décide pour vous hyper seulaxumPoem · Salvo RocketLoco · CotLeptos · Dioxus vous écrivez toutroutage, extracteurs+ OpenAPI, TLS… + conventions, macros+ ORM, tâches, e-mails+ l’interface le kit le meuble monté
Aucun point de l’éventail n’est meilleur qu’un autre. À gauche, la liberté et la maîtrise ; à droite, la vitesse et la cohérence.

Vers la droite, on gagne du temps au début, et l’on dépend des choix de quelqu’un d’autre : Loco a choisi SeaORM pour vous, un cadeau si vous n’aviez pas d’avis, une contrainte si vous préfériez sqlx. Vers la gauche, on écrit plus de code d’assemblage, mais ce code est le vôtre. Et les deux axes sont indépendants : Loco est loin sur celui-ci, mais tout près des fondations sur le premier, puisqu’il est bâti sur axum.

Les six autres critères

Une fois ces deux axes posés, reste à savoir ce que vaut chaque framework au quotidien. Six critères le disent.

3

La maintenance et la gouvernance

La probabilité que le framework soit encore maintenu quand votre service aura cinq ans. En Rust, beaucoup de projets reposent sur quelques personnes : Rocket a passé vingt-neuf mois sans version avant la 0.5.2, Poem n’a rien publié depuis juillet 2025, Tide s’est arrêté avec sa fondation.

Ma règle : un projet porté par une organisation, avec plusieurs mainteneurs actifs et des versions régulières, plutôt qu’un projet brillant porté par une seule personne.

4

La stabilité de l’API

Une crate en 0.x peut casser son API à chaque version mineure. axum est toujours en 0.x ; Actix Web est en version 4 depuis 2022, Salvo et Loco viennent de passer en 1.0. Les ruptures d’axum sont rares (environ une par an) et souvent mécaniques, mais sur dix services, elles se paient dix fois.

À prévoir : une montée de version par an et par service, dans le planning, et un Cargo.lock pour ne jamais la subir par surprise.

5

Le modèle d’exécution

Avec tokio en mode multi-thread (axum, Poem, Salvo, Rocket), une tâche peut changer de cœur à chaque .await : ce qu’elle garde doit pouvoir voyager entre les threads (être Send). Actix Web, avec un runtime par cœur, ne l’exige pas pour vos handlers.

Pour une équipe qui découvre Rust : c’est le compilateur qui la protège d’un bug de concurrence, mais c’est l’une des erreurs les plus fréquentes au début. Expliquez-la avant qu’elle ne décourage.

6

Les erreurs de compilation

Ce qui décide de la productivité d’une équipe qui apprend. Les frameworks à traits génériques (axum, warp) produisent des erreurs longues ; ceux à macros (Rocket), des erreurs qui passent par la macro. axum a une réponse simple, #[debug_handler], qui pointe le paramètre fautif.

Le test que je recommande : une route avec une erreur volontaire, écrite par quelqu’un qui découvre le framework. Chronométrez le temps qu’il met à comprendre le message.

7

Le contrat d’API

Si d’autres équipes consomment votre API, sa documentation OpenAPI fait partie du produit. poem-openapi la déduit directement des types ; utoipa fonctionne avec axum, Actix Web et Rocket ; aide est pensé pour axum ; Salvo a son propre module, salvo-oapi.

Mon conseil : décidez dès le premier jour si la spécification est générée par le code ou écrite à la main. Les deux se défendent ; les mélanger ne se défend pas.

8

L’équipe : apprendre, recruter, se faire aider

Zero To Production in Rust enseigne Actix Web, le guide de Rocket est un modèle du genre, le dépôt d’axum regorge d’exemples. Un développeur backend Rust connaît presque toujours axum ou Actix Web. Les assistants de code aussi, mais ils mélangent les versions : celui qui propose /:id pour axum 0.8 a appris sur du code d’avant 2025.

À vérifier : qui maintiendra ce code dans trois ans, et ce que ces personnes connaissent déjà.

Et la vitesse ?

Tous ces frameworks sont très rapides. Sur un serveur qui renvoie une chaîne de caractères, Actix Web tire souvent un léger avantage de son runtime par cœur ; sur un vrai service, qui interroge une base et sérialise du JSON, ces écarts se noient dans le reste. La vitesse brute rejoint donc ce qui ne devrait pas peser :

Les benchmarks « hello world ». Les frameworks Rust se sont longtemps disputé le haut des classements TechEmpower, archivés en mars 2026. Ils mesuraient des serveurs qui renvoient une chaîne de caractères. Le vôtre attendra une base de données : c’est elle qui décidera.
Les étoiles sur GitHub. Elles mesurent l’enthousiasme d’un moment, pas la santé d’un projet. Le rythme des versions et le nombre de mainteneurs en disent bien plus.
La beauté du « hello world ». Tous les frameworks sont élégants sur dix lignes. La question est ce qu’ils deviennent à dix mille, avec l’authentification, les erreurs et les tests.
Le premier tutoriel qu’on a lu. On garde souvent le framework par lequel on a appris Rust. C’est humain, et c’est une très mauvaise raison d’y engager une équipe.

Si la performance compte vraiment, une seule méthode : un prototype de l’endpoint le plus chargé, dans deux frameworks, avec la vraie base et une charge réaliste. Une journée de travail, qui vaut mieux que cent benchmarks lus sur Internet.

$ cargo tree --depth 1 # ce que chaque ligne du fichier tire avec elle : la distance, vue par Cargo
[criteria] · ce qu’on retient

Huit critères, dont deux qui dessinent la carte, et une vitesse remise à sa place. On peut enfin poser la bonne question.

[question] · La bonne question
Partie 4 · [question]

La bonne question

Avec ces critères en main, on peut reformuler la question de départ.

La question qu’on pose partout Quel est le meilleur framework web Rust ?
La question à se poser De quoi mon équipe a-t-elle besoin au-dessus des fondations communes, et combien coûtera la sortie si je dois changer d’avis ?

La première question n’a pas de réponse : les comparatifs alignent des benchmarks et concluent par « ça dépend », sans jamais dire de quoi. La seconde en a une, différente pour chaque projet. Je dirai aussi lequel j’aime le plus, et pourquoi.

Chaque moitié de la bonne question se pèse avec les critères de la partie 3 :

Première moitié · ce qu’on gagne De quoi mon équipe a-t-elle besoin au-dessus des fondations communes ?

Elle se pèse avec les critères 2 à 8 :

  • 2intégration
  • 3maintenance
  • 4stabilité
  • 5exécution
  • 6erreurs
  • 7contrat
  • 8équipe
Seconde moitié · ce qu’on paiera Combien coûtera la sortie si je dois changer d’avis ?

Elle se lit avec le critère 1 :

  • 1la distance aux fondations communes
La règle qui en découle : prenez l’outil le plus proche des fondations communes qui couvre vos besoins.

Pour l’appliquer, sept questions concrètes, à poser en équipe avant d’écrire la première route. Les réponses désignent presque toujours le bon candidat :

  1. Qu’est-ce que les fondations ne me donnent pas ?critères 2 à 8Faites-en la liste, par écrit. Si elle est courte, axum et quelques crates suffisent peut-être.
  2. Combien de décisions voulons-nous confier au framework ?critère 2Configuration, base de données, tâches de fond, e-mails : une équipe pressée gagne à les déléguer, une équipe qui a des avis gagne à les garder.
  3. Aurons-nous besoin de gRPC, du serverless, d’un middleware que nous n’écrirons pas ?critère 1Si la réponse est « peut-être », la famille tower est une assurance qui ne coûte presque rien.
  4. Qui consomme cette API ?critère 7Vos propres applications, ou des partenaires ? Si un contrat publié est un livrable, il peut choisir le framework à votre place.
  5. Qui maintiendra ce code ?critères 5, 6 et 8Pas l’équipe d’aujourd’hui : celle de dans trois ans. Ce qu’elle sait de Rust, et ce qu’elle trouvera facilement en ligne.
  6. Qui maintient l’outil, et à quel rythme casse-t-il ?critères 3 et 4Date de la dernière version, nombre de mainteneurs actifs, numéro de version : 0.x ou 1.0.
  7. Mon code métier importe-t-il le framework ?critère 1La réponse doit être non, quel que soit l’outil choisi. C’est la seule règle qui vaut pour tous les candidats.
# La ligne qui manque, enfin. Mais laquelle ? # axum = ? actix-web = ? rocket = ? poem = ? salvo = ? loco-rs = ?
[question] · ce qu’on retient

La question est posée, la grille est prête. Voyons ce que chaque candidat y répond.

[frameworks] · Les candidats
Partie 5 · [frameworks]

Les candidats, un par un

Six candidats, rangés du plus proche des fondations communes au plus lointain. Pour chacun : une présentation, sa position sur les huit critères, les situations où il est le plus à sa place, ses défis et ses risques, et mon verdict.

La carte des candidats, selon les deux premiers critères. Horizontalement, la distance aux fondations communes, de nulle à très élevée ; verticalement, le niveau d’intégration, du kit au meuble monté. hyper seul : distance nulle, tout en bas. axum : distance faible, peu intégré. Loco : distance faible, très intégré. Poem : distance modérée, intégration moyenne. Rocket et Salvo : distance élevée, intégration moyenne à forte. Actix Web : distance très élevée, intégration moyenne. Une bande couvre les distances nulle et faible : la famille tower. la famille tower on hérite de tout l’écosystème nullefaiblemodéréeélevéetrès élevée distance aux fondations communes (critère 1) → le kit le meuble monté niveau d’intégration (critère 2) → hyper seul axum Loco Poem Salvo Rocket Actix Web
Les deux premiers critères, pour les six candidats. De gauche à droite, un framework remplace de plus en plus de fondations par les siennes ; de bas en haut, il décide de plus en plus de choses pour vous. Positions appréciées, pas mesurées. Un clic sur un nom mène à sa fiche.

Dans chaque fiche, le même endpoint, replié : GET /articles/{id}, qui lit un article dans une base partagée et renvoie du JSON, ou une 404. Le type Db est le même partout : seul le framework change.

axum

0.8.9 · l’équipe tokioMon coup de cœurdistance faible

Le plus petit framework possible au-dessus de tower. Il ne réinvente rien : il apporte le routeur et les extracteurs, l’étape 4 du trajet, et laisse tout le reste aux fondations communes. Publié en 2021 seulement, il est arrivé sur un écosystème qui n’attendait qu’un routeur.

async fn(State<Db>, Path<u64>) -> Result<Json<Article>, StatusCode>

Une précision : c’est mon coup de cœur. J’ai longtemps utilisé Actix Web ; je l’aime, et je le recommande toujours, surtout pour sa maturité. Mais axum m’a fait un coup de cœur. Je ne l’aime pas axum parce qu’il fait beaucoup. Je l’aime parce qu’il fait exactement ce qui manque aux fondations, et rien d’autre : c’est la plus petite distance qu’on puisse prendre avec elles tout en gagnant vraiment quelque chose. Cinq raisons :

  1. Il s’efface. Un handler est une fonction asynchrone ordinaire, sans macro ni cycle de vie caché : une nouvelle recrue lit du Rust, pas du « axum ».
  2. Les types sont le contrat. La signature dit ce que le handler attend et ce qu’il rend ; le compilateur vérifie la plomberie, et je garde mon attention pour le métier.
  3. Il parle tower, donc il parle à tout. tonic sur le même port, tower-http, AWS Lambda ou Cloudflare quand il le faut.
  4. Il est maintenu par ceux qui maintiennent les fondations. Quand tokio, hyper et tower évoluent, il évolue avec eux, pas six mois plus tard.
  5. Il grandit avec le projet. Loco, Cot, Leptos et Dioxus sont bâtis dessus : on ne le quitte pas, on monte d’un étage.

Sur les huit critères

1 · Distancefaible : tower de bout en bout
2 · Intégrationun kit : tout à choisir
3 · Maintenancel’équipe de tokio
4 · Stabilité0.x : une rupture par an
5 · Exécutionmulti-thread : tâches Send
6 · Erreursobscures, sauf #[debug_handler]
7 · Contratutoipa ou aide, à côté
8 · Équipele plus répandu
Là où il est à sa place
  • une API REST ou un microservice qui doit durer ;
  • un backend gRPC avec quelques routes REST, tonic sur le même port ;
  • une fonction serverless : lambda_http et workers-rs exécutent un routeur axum ;
  • le serveur d’une application fullstack Leptos ou Dioxus.
Défis et risques
  • toujours en 0.x : la 0.7 a cassé pour passer à hyper 1, la 0.8 pour la syntaxe des chemins (/:id devenu /{id}) ; la 0.9 se prépare, sans date ;
  • quand un handler ne respecte pas le contrat, l’erreur de compilation est obscure ;
  • configuration, base de données, authentification, documentation : tout est à choisir ;
  • les tâches doivent être Send, une contrainte qui surprend au début.
Verdict

Mon point de départ par défaut, pour une API comme pour un microservice. Ce qu’il me coûte, je le compense : la montée de version annuelle est planifiée comme une tâche normale, #[debug_handler] est activé en développement, et un modèle de projet interne fixe une fois pour toutes la configuration, les erreurs et la base de données. Mon propre « framework », en somme, dans un Cargo.toml.

tonic0.14 · le voisin gRPCUn service tower : un routeur axum peut lui céder les requêtes gRPC sur le même port. Il a rejoint le projet gRPC en mai 2026.
LeptosDioxusfullstackL’interface aussi en Rust, compilée en WebAssembly, avec des « fonctions serveur » appelées depuis le navigateur. Le serveur, c’est axum ; Dioxus vise aussi le mobile et le bureau.
Le code : GET /articles/{id} avec axum 0.8
async fn article(
    State(db): State<Arc<Db>>,
    Path(id): Path<u64>,
) -> Result<Json<Article>, StatusCode> {
    db.find(id).await.map(Json).ok_or(StatusCode::NOT_FOUND)
}

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/articles/{id}", get(article))
        .with_state(Arc::new(Db::connect().await));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Aucune macro sur le handler. Le type de retour est le contrat : du JSON, ou un code d’erreur. Le serveur, c’est tokio qu’on voit à l’œuvre, sans intermédiaire.

Loco

1.2 · le Rails de Rustdistance faible

Un framework d’application bâti sur axum : des conventions, des générateurs et des batteries. Il se présente comme le framework pour une personne. Il ne remplace aucune fondation ; il ajoute tout ce qu’axum vous laisse choisir : modèles et migrations (SeaORM), tâches de fond, e-mails, authentification.

async fn(State<AppContext>, Path<i32>) -> Result<Response>

Sur les huit critères

1 · Distancefaible : axum en dessous
2 · IntégrationORM, tâches, e-mails, générateurs
3 · Maintenanceune version par mois
4 · Stabilité1.x depuis juillet 2026
5 · Exécutioncelui d’axum : Send
6 · Erreurscelles d’axum, et les conventions
7 · Contratles crates d’axum
8 · Équipedes conventions à apprendre
Là où il est à sa place
  • un produit complet (pages, back-office, tâches, e-mails) porté par une petite équipe ;
  • un MVP qui doit sortir vite ;
  • une équipe venue de Rails ou de Django ;
  • une équipe qui débute en Rust et préfère qu’on décide pour elle.
Défis et risques
  • des choix déjà faits : si vous n’aimez pas SeaORM, vous luttez contre le framework ;
  • plus de dépendances, donc une compilation plus longue et plus de code à auditer ;
  • des conventions à apprendre, en plus de Rust.
Verdict

Le meilleur raccourci de l’écosystème quand on construit un produit entier à peu de mains. Et un raccourci sans impasse : le jour où une convention gêne, on redescend d’un étage, dans axum.

Cot0.7 · le pari du Django de RustUn ORM qui déduit tables et migrations de vos types, et un panneau d’administration prêt à l’emploi. Bâti sur axum lui aussi, encore jeune.
Le code : GET /articles/{id} avec Loco… sans l’écrire
# installer l’outil, puis créer le projet (il pose quelques questions : base, type d’application)
cargo install loco
loco new

# générer le modèle, la migration, le contrôleur et ses routes
cargo loco generate scaffold article titre:string! contenu:text --api
cargo loco start

Pas une ligne de handler à écrire pour ce premier endpoint : le générateur crée la lecture, et aussi la liste, la création, la modification et la suppression. Le contrôleur généré est un handler axum ordinaire, que l’on modifie ensuite à la main.

Poem

3.1 · le contrat déduit des typesdistance modérée

Proche d’axum dans l’esprit, avec ses propres middlewares (et une passerelle vers tower), et une pièce maîtresse : poem-openapi, qui fait de vos types Rust la source unique du contrat OpenAPI. La spécification et l’interface Swagger sont générées depuis le code, donc jamais désynchronisées.

#[handler] async fn(Data<&Db>, Path<u64>) -> poem::Result<Json<Article>>

Sur les huit critères

1 · Distancemodérée : ses middlewares, une passerelle
2 · IntégrationOpenAPI, gRPC, Lambda en modules
3 · Maintenancerien depuis juillet 2025
4 · Stabilitév3
5 · Exécutionmulti-thread : Send
6 · Erreursvia la macro #[handler]
7 · Contratpoem-openapi : les types
8 · Équipeune petite communauté
Là où il est à sa place
  • une API dont le contrat OpenAPI est le produit ;
  • des services qui veulent gRPC, AWS Lambda, voire un serveur MCP pour les agents IA, en modules officiels ;
  • une équipe qui vient d’axum : l’API se prend en main vite.
Défis et risques
  • aucune version depuis juillet 2025 ; Poem 4 est annoncé dans le dépôt, mais n’est pas publié ;
  • une communauté plus petite, donc moins de réponses toutes faites ;
  • ses middlewares natifs ne se branchent que chez lui.
Verdict

Le plus élégant pour une API dont le contrat est le livrable. Mais tant que Poem 4 n’est pas sorti, je préfère axum avec utoipa, pour la longévité, en acceptant un peu plus d’annotations. À reconsidérer le jour où le projet publie de nouveau.

Le code : GET /articles/{id} avec Poem 3
#[handler]
async fn article(Data(db): Data<&Arc<Db>>, Path(id): Path<u64>) -> poem::Result<Json<Article>> {
    db.find(id).await.map(Json).ok_or_else(|| Error::from_status(StatusCode::NOT_FOUND))
}

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    let app = Route::new()
        .at("/articles/:id", get(article))
        .data(Arc::new(Db::connect().await));
    Server::new(TcpListener::bind("0.0.0.0:3000")).run(app).await
}

Presque le même code qu’axum, avec une macro #[handler] et l’ancienne syntaxe de chemin (:id). La différence se verrait avec poem-openapi : les mêmes types produiraient aussi la documentation de l’API.

Salvo

1.0 · batteries inclusesdistance élevée

Le plus « batteries incluses » des frameworks HTTP. Un modèle mental unique, où tout est handler, middlewares compris, et beaucoup de choses fournies d’office : HTTP/3 (via quinn), certificats TLS automatiques (ACME), OpenAPI, proxy, limitation de débit.

#[handler] async fn(req: &mut Request, depot: &mut Depot, res: &mut Response)

Sur les huit critères

1 · Distanceélevée : ses propres middlewares
2 · IntégrationHTTP/3, TLS, OpenAPI, proxy
3 · Maintenancetrès actif
4 · Stabilité1.x depuis septembre 2026
5 · Exécutionmulti-thread : Send
6 · Erreursvia la macro #[handler]
7 · Contratsalvo-oapi, intégré
8 · Équipepeu connu
Là où il est à sa place
  • un service HTTP autonome qui veut tout dans une seule dépendance, du TLS automatique à HTTP/3 ;
  • une équipe à l’aise en Rust, prête à suivre un projet plus confidentiel.
Défis et risques
  • une centaine de versions 0.x avant la 1.0 : une API qui a beaucoup bougé ;
  • hors de la famille tower : ses middlewares ne servent que chez lui ;
  • moins connu des développeurs que vous recruterez.
Verdict

La 1.0 de septembre 2026 en fait un choix défendable pour un service autonome. Je le prendrais quand ses batteries remplacent vraiment plusieurs crates, en regardant de près le rythme de ses premières versions 1.x.

Le code : GET /articles/{id} avec Salvo 1
#[handler]
async fn article(req: &mut Request, depot: &mut Depot) -> Result<Json<Article>, StatusError> {
    let id: u64 = req.param("id").ok_or_else(StatusError::bad_request)?;
    let db = depot.obtain::<Arc<Db>>().map_err(|_| StatusError::internal_server_error())?;
    db.find(id).await.map(Json).ok_or_else(StatusError::not_found)
}

#[tokio::main]
async fn main() {
    let db = Arc::new(Db::connect().await);
    let router = Router::new()
        .hoop(affix_state::inject(db))
        .push(Router::with_path("articles/{id}").get(article));
    let acceptor = TcpListener::new("0.0.0.0:3000").bind().await;
    Server::new(acceptor).serve(router).await;
}

Pas d’extracteurs ici : le handler reçoit la requête et un « dépôt » (Depot) où les middlewares rangent l’état. Le middleware qui y injecte la base, affix_state::inject, est lui-même un handler, accroché par hoop : tout est handler.

Rocket

0.5.2 · le plus agréable à écriredistance élevée

Des macros partout, des conventions fortes, une ergonomie pensée comme un produit : le framework qui a montré que Rust pouvait être aussi confortable que Rails ou Flask. Ses request guards font de l’authentification un type en paramètre, vérifié avant même d’entrer dans le handler.

#[get("/articles/<id>")] async fn(&State<Db>, u64) -> Option<Json<Article>>

Sur les huit critères

1 · Distanceélevée : fairings, cycle de vie propre
2 · Intégrationconventions, configuration, gardes
3 · Maintenance29 mois sans version, avant la 0.5.2
4 · Stabilité0.x
5 · Exécutionmulti-thread : Send
6 · Erreurselles passent par les macros
7 · Contratutoipa, à côté
8 · Équipele plus facile à apprendre
Là où il est à sa place
  • apprendre ou enseigner le web en Rust, avec l’un des meilleurs guides officiels de l’écosystème ;
  • un prototype, ou un outil interne qui restera petit ;
  • quand le plaisir d’écriture compte plus que la longévité.
Défis et risques
  • le rythme : 0.5.1 en mai 2024, 0.5.2 en octobre 2026, pour un projet porté pour l’essentiel par une seule personne ; des forks communautaires ont publié leurs propres préversions 0.6 ;
  • hors de la famille tower : ses fairings sont un monde à part ;
  • la magie des macros a son prix : des erreurs moins directes, une compilation plus lourde.
Verdict

Le meilleur pour faire aimer le web en Rust. Pour un produit qui doit durer, je reviendrais à axum. La 0.5.2, sortie il y a quelques jours, est un signe de vie à suivre de près.

Le code : GET /articles/<id> avec Rocket 0.5
#[get("/articles/<id>")]
async fn article(db: &State<Db>, id: u64) -> Option<Json<Article>> {
    db.find(id).await.map(Json)
}

#[launch]
async fn rocket() -> _ {
    rocket::build()
        .manage(Db::connect().await)
        .mount("/", routes![article])
}

Le plus court des six. None devient une 404 tout seul, le port vient de la configuration (Rocket.toml), et la macro #[launch] écrit le main à votre place. Tout est concis, et une partie de ce qui se passe est cachée.

Actix Web

4.18 · le vétérandistance très élevée

Un framework complet et autonome, pensé pour la performance. Il remplace les étapes 2 et 3 du trajet par les siennes, avec son propre moteur HTTP (actix-http) et son propre modèle de middleware, et utilise tokio à sa façon : un petit runtime indépendant par cœur.

#[get("/articles/{id}")] async fn(web::Data<Db>, web::Path<u64>) -> impl Responder

Sur les huit critères

1 · Distancetrès élevée : son serveur, ses middlewares
2 · Intégrationsessions, CORS, identité en modules
3 · Maintenancetrès actif
4 · Stabilitév4 depuis 2022
5 · Exécutionun runtime par cœur : pas de Send
6 · Erreurssans outil dédié
7 · Contratutoipa, à côté
8 · Équipetrès connu, Zero To Production
Là où il est à sa place
  • un service où chaque microseconde compte : passerelle, proxy, publicité en temps réel, serveur de jeu, mesure à l’appui ;
  • un projet qui veut une base mûre et une API stable, sans montée de version annuelle ;
  • une équipe qui le maîtrise déjà, ou du code existant ;
  • un service sans gRPC en vue.
Défis et risques
  • hors de la famille tower : des middlewares plus verbeux, et ceux de tower-http inutilisables tels quels ;
  • gRPC vit à côté, sur un autre serveur, pas dedans ;
  • une histoire mouvementée : en janvier 2020, son créateur a quitté le projet après une polémique sur l’usage de code unsafe, avant que la communauté ne le reprenne. Elle rappelle ce qu’est le « facteur bus ».
Verdict

Je l’ai longtemps utilisé, et je le recommande toujours, d’abord pour sa maturité : stable depuis 2022, très actif, très rapide, et enseigné par Zero To Production in Rust. Une équipe qui le maîtrise n’a aucune raison d’en changer pour suivre la mode. Je lui préfère axum quand gRPC, le serverless ou les middlewares de tower-http sont en vue ; et si c’est le débit qui vous le fait choisir, mesurez l’écart sur une vraie charge : il est souvent plus faible que prévu.

Le code : GET /articles/{id} avec Actix Web 4
#[get("/articles/{id}")]
async fn article(db: web::Data<Db>, id: web::Path<u64>) -> impl Responder {
    match db.find(id.into_inner()).await {
        Some(a) => HttpResponse::Ok().json(a),
        None => HttpResponse::NotFound().finish(),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let db = web::Data::new(Db::connect().await);
    HttpServer::new(move || App::new().app_data(db.clone()).service(article))
        .bind(("0.0.0.0", 3000))?
        .run()
        .await
}

La route est une macro, l’état passe par web::Data. Remarquez la fermeture donnée à HttpServer::new : elle est appelée une fois par cœur, pour construire une application par worker. C’est le modèle « un runtime par cœur », visible dans le code.

Les autres, à connaître

À surveillerPavex

De Luca Palmieri, l’auteur de Zero To Production. L’injection de dépendances résolue à la compilation, et des erreurs pensées pour les humains. Une idée forte, mais une version 0.2 qui n’a pas bougé depuis novembre 2025 : pas pour la production d’aujourd’hui.

En retraitwarp

L’élégance des filtres, par l’auteur de hyper. Toujours maintenu (0.4 en 2025), mais l’écosystème l’a doublé : ses idées vivent aujourd’hui dans axum.

La leçonTide

Construit sur async-std, sans version depuis décembre 2021. Un bon framework, emporté par sa fondation. Avant de regarder un framework, regardez ce sur quoi il repose.

Avant d’adopter un outil hors de cette liste, regardez trois choses : la date de sa dernière version, le nombre de personnes qui fusionnent les contributions, et ce qu’il faut implémenter pour écrire un middleware.

Les téléchargements mentent un peu

Reste la question que tout le monde se pose : qui est le plus utilisé ? crates.io publie le nombre de téléchargements de chaque crate sur les quatre-vingt-dix derniers jours. Le résultat est sans appel, en apparence :

Téléchargements des quatre-vingt-dix derniers jours, relevés sur crates.io le 28 octobre 2026. Un téléchargement n’est pas un projet : chaque compilation en intégration continue en compte un. Hachurée : axum, moins la totalité des téléchargements de tonic.

Ces chiffres mesurent l’écosystème plus que le choix. tonic, le framework gRPC, embarque axum par défaut, et pèse à lui seul 102,7 millions de téléchargements ; les méta-frameworks l’embarquent aussi. Retirez tout tonic, ce qui exagère la correction : il reste environ 41 millions, plus de trois fois Actix Web. L’écart est réel, mais moindre qu’il n’y paraît. Il dit surtout qu’axum est devenu une infrastructure, pas seulement un framework.

$ cargo add axum # Mon point de départ, quand rien ne départage. Pas toujours le bon.
[frameworks] · ce qu’on retient

Six candidats, six distances, et aucun qui gagne partout. Pour trouver le vôtre, place à la boussole.

[compass] · La boussole
Partie 6 · [compass]

La boussole

Les fiches partent des candidats ; la boussole fait le chemin inverse. Elle part de votre projet, pose quatre questions et propose un point de départ : un framework, une alternative, et le bloc [dependencies] de votre Cargo.toml. Elle ne remplace pas le raisonnement des parties précédentes : elle le résume.

Quatre questions, un point de départ

C’est ma grille de lecture, pas une vérité. Chacun des six candidats peut en sortir : tout dépend de votre projet et de votre équipe.

1. Vous construisez…
2. Votre équipe et Rust ?
3. Elle connaît déjà…
4. Ce qui compte le plus ?
Point de départ conseillé

Répondez aux questions ci-contre.

    
            

    Jouez avec les réponses : la ligne du framework change d’un cas à l’autre, les fondations presque jamais. C’est exactement ce que disait la partie 2.

    $ cargo add … # La ligne que la boussole vous a donnée. Les fondations, elles, ne bougent pas.
    [compass] · ce qu’on retient

    La boussole vous donne un point de départ. Reste à retenir l’essentiel.

    [summary] · Ce qu’il faut retenir
    Partie 7 · [summary]

    Ce qu’il faut retenir

    Si vous ne deviez garder qu’une page de cet article, ce serait celle-ci.

    1. En Rust, les fondations font l’essentiel

      Le runtime, le serveur HTTP, les middlewares : tokio, hyper et tower les fournissent à presque tous. Le framework n’est qu’une couche fine par-dessus.

    2. Le critère par excellence : la distance

      Plus un framework remplace ces fondations par les siennes, plus il en fait à sa façon, moins il hérite de l’écosystème, et plus la sortie coûte. Son modèle de middleware le dit en une ligne.

    3. La plus petite distance qui vous suffit

      Listez ce que les fondations ne vous donnent pas, choisissez ce que vous voulez confier au framework, prenez l’outil le plus proche qui couvre tout cela, et gardez le métier à l’abri.

    Le fichier, une fois rempli

    Voici, concrètement, mon point de départ par défaut. C’est le Cargo.toml de mon-api, vide au début de l’article, une fois rempli :

    mon-api/Cargo.toml, mon point de départ (octobre 2026)
    [dependencies]
    # Les fondations (partie 2) : elles ne changent presque jamais.
    tokio = { version = "1", features = ["full"] }
    serde = { version = "1", features = ["derive"] }
    tracing = "0.1"
    tracing-subscriber = { version = "0.3", features = ["env-filter"] }
    
    # Le framework (partie 5) : une ligne, et elle est remplaçable.
    axum = "0.8"
    tower-http = { version = "0.7", features = ["trace", "cors", "compression-gzip"] }
    
    # Mes briques (partie 3) : ce qu’un framework intégré aurait choisi pour moi.
    sqlx = { version = "0.9", features = ["runtime-tokio", "postgres"] }
    utoipa = "6"
    thiserror = "2"

    Regardez où est le framework : une ligne sur neuf. Le reste, ce sont des fondations que vous garderiez avec n’importe quel autre choix, et des briques que vous avez choisies vous-même. C’est pour cela qu’aucun ne s’impose : le vrai choix se joue sous le framework. En Rust, votre framework n’est pas une ligne de ce fichier. C’est le fichier tout entier.

    Ce que je surveille

    axum 0.9, en préparation : ce qu’elle cassera, et si elle ouvre la voie à une 1.0.
    Le réveil de Rocket : la 0.5.2 sera-t-elle suivie d’un rythme régulier ?
    Poem 4, annoncé : s’il sort, poem-openapi redevient un choix de premier plan.
    Pavex : l’injection compilée tiendra-t-elle ses promesses à grande échelle ?
    gRPC en Rust : tonic a rejoint le projet gRPC en mai 2026, et une crate officielle doit lui succéder.

    À vous de jouer

    Je n’ai donné que ma lecture, et mes préférences. J’aimerais beaucoup connaître les vôtres.

    Avez-vous déjà dû quitter un framework Rust ? Qu’a-t-il fallu réécrire ?

    Vos middlewares sont-ils des couches tower, ou propres à votre framework ? L’avez-vous choisi en le sachant ?

    Avez-vous déjà vu le framework être le vrai goulot de performance, profil à l’appui ?

    Code d’abord ou contrat d’abord : lequel tient le mieux dans la durée chez vous ?

    Un framework intégré comme Loco, Salvo ou Rocket vous a-t-il fait gagner, ou perdre, du temps sur la longueur ?

    Et vous, quel est votre coup de cœur, et dans quels cas ne le prenez-vous pas ?

    Pour me répondre, écrivez-moi par e-mail ou sur LinkedIn. Je réponds avec plaisir, selon mon agenda, parfois avec un peu de retard, mais je réponds toujours.

    Le mot de la fin
    En Rust, on n’épouse pas un framework comme on épouse une plateforme. On hérite de fondations, puis on choisit un style. Votre Cargo.toml raconte ce choix : lisez-le comme une décision d’architecture.
    Rivel Babindamana
    Ingénieur, octobre 2026

    PS : si vous maintenez l’un des frameworks de cet article et que je vous ai mal compris, écrivez-moi. Je corrigerai, avec plaisir.