La concurrence sans threads : attendre sans bloquer, pas calculer en parallèle.Concurrency without threads: waiting without blocking, not computing in parallel.
Le Vol 7 parlait de parallélisme : faire tourner plusieurs calculs en même temps, sur plusieurs cœurs. L'async répond à un besoin différent, qu'on confond sans cesse avec celui-là : attendre sans bloquer. Pense à un serveur qui tient dix mille connexions, dont chacune dort la plupart du temps en attendant le réseau. Lancer dix mille threads serait un gâchis — chacun coûte sa pile, son changement de contexte. On veut une poignée de threads qui jonglent entre des milliers de tâches en sommeil, reprenant chacune juste quand sa donnée arrive. C'est l'async. En Rust, une async fn ne retourne pas sa valeur : elle retourne un Future, une valeur qui décrit le calcul à mener. Et .await est l'opérateur qui dit « mène ce calcul à terme, mais si tu dois attendre, rends la main pour qu'autre chose avance ».Vol 7 spoke of parallelism: running several computations at once, on several cores. Async answers a different need, constantly confused with that one: waiting without blocking. Picture a server holding ten thousand connections, each asleep most of the time waiting on the network. Spawning ten thousand threads would be waste — each costs its stack, its context switch. You want a handful of threads juggling thousands of sleeping tasks, resuming each just when its data arrives. That's async. In Rust, an async fn doesn't return its value: it returns a Future, a value describing the computation to carry out. And .await is the operator that says 'carry this computation to completion, but if you must wait, yield so something else can advance'.
■ actif · ▢ en attente (.await) → la main passe à une autre tâche■ active · ▢ waiting (.await) → control passes to another task
■ calcul plein → vrai parallélisme, sur plusieurs cœurs■ full computation → true parallelism, across cores
// async fn ne RETOURNE pas un T — il retourne un Future<Output = T> async fn charger(url: &str) -> String { // … une requête réseau : on ATTEND la réponse sans bloquer le thread reponse } #[tokio::main] // le runtime : il fournit l'exécuteur qui fait tourner les Futures async fn main() { let page = charger("/index").await; // .await : suspend CETTE tâche jusqu'à ce que le Future soit prêt println!("{}", page.len()); // … et pendant l'attente, l'exécuteur fait avancer d'autres tâches }
Trois éléments à repérer. async fn transforme le type de retour : charger ne rend pas un String mais un Future<Output = String>. .await est le point où l'on consomme ce Future — en suspendant la tâche courante le temps de l'attente, sans bloquer le thread. Et #[tokio::main] installe le runtime : la bibliothèque qui fournit l'exécuteur, le moteur qui fait réellement tourner les Future. La std définit le trait Future, mais ne fournit aucun exécuteur — ce choix t'appartient (tokio, async-std…). Sans runtime, un Future n'a personne pour le faire avancer.Three elements to spot. async fn transforms the return type: charger returns not a String but a Future<Output = String>. .await is where you consume that Future — suspending the current task for the wait, without blocking the thread. And #[tokio::main] installs the runtime: the library providing the executor, the engine that actually runs the Futures. The std defines the Future trait, but ships no executor — that choice is yours (tokio, async-std…). Without a runtime, a Future has no one to drive it.
« Est-ce que j'attends une I/O, ou est-ce que je veux du calcul en parallèle ? » C'est la question qui tranche entre async et threads, et la confondre coûte cher dans les deux sens. Des milliers d'attentes réseau → async, un runtime, une poignée de threads. Du calcul lourd à répartir sur les cœurs → threads (Vol 7), sans runtime async. L'async ne rend aucun calcul plus rapide : il permet seulement de ne pas gaspiller un thread à ne rien faire pendant qu'on attend.'Am I waiting on I/O, or do I want computation in parallel?' That's the question deciding between async and threads, and confusing it costs dearly both ways. Thousands of network waits → async, a runtime, a handful of threads. Heavy computation to spread across cores → threads (Vol 7), no async runtime. Async makes no computation faster: it only lets you not waste a thread doing nothing while you wait.
Le vocabulaire qui structure tout le volume. Un programme est I/O-bound quand son temps se passe surtout à attendre quelque chose d'extérieur — réseau, disque, base de données : le processeur est oisif, c'est l'async qui brille. Il est CPU-bound quand son temps se passe à calculer — compression, rendu, simulation : le processeur est saturé, ce sont les threads et les cœurs qu'il faut. La même application peut avoir les deux profils selon l'endroit ; le réflexe est de toujours savoir, à un point donné du code, lequel domine.The vocabulary that structures the whole volume. A program is I/O-bound when its time is spent mostly waiting on something external — network, disk, database: the CPU is idle, async shines. It's CPU-bound when its time is spent computing — compression, rendering, simulation: the CPU is saturated, threads and cores are what you need. The same application can have both profiles depending on the place; the reflex is to always know, at a given point in the code, which dominates.