Un atelier Rust · Édition d'ApprentissageA Rust Workshop · Learning Edition

Le problème des fonctions colorées The colored functions problem

async contamine la signature de tout ce qui l'appelle — et la donnée tenue à travers un .await doit être Send. Le Vol 7 ressurgit en plein async. async contaminates the signature of everything that calls it — and data held across a .await must be Send. Vol 7 resurfaces mid-async.

AudienceAudience
Dev connaissant Future/.await (Vol 8 №1), Send et les threads (Vol 7) Dev who knows Future/.await (Vol 8 №1), Send and threads (Vol 7)
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●●● AsyncColoringSend

Chapitre 1 en accès libre — la suite (ch. 2 à 5) est réservée. Chapter 1 free to read — the rest (ch. 2–5) is members-only.

01CadrageFraming3 min

Le mot-clé ne reste pas où on le pose : async remonte toute la pile d'appels.The keyword doesn't stay put: async climbs the whole call stack.

Le numéro précédent a regardé un Future seul, comme s'il vivait isolé. Il n'en est rien : appeler de l'async a une conséquence qui déborde la fonction. Pour obtenir la valeur d'un Future, on l'await ; mais .await n'est permis que dans un contexte async. Donc une fonction qui await devient elle-même async, et celle qui l'appelle aussi, et ainsi de suite jusqu'au main. C'est ce qu'on appelle le « problème des fonctions colorées » : il existe deux couleurs de fonctions, synchrone et asynchrone, et la couleur async se propage de proche en proche dès qu'on l'introduit quelque part. On ne peut pas appeler simplement une async fn depuis du code synchrone — les deux mondes ne se composent pas librement. C'est la friction promise à la fin du №1 : la couleur a un prix, et il se paie sur toute la chaîne d'appels.The previous issue looked at a Future alone, as if it lived in isolation. It doesn't: calling async has a consequence that overflows the function. To get a Future's value, you await it; but .await is only allowed in an async context. So a function that awaits becomes async itself, and its caller too, and so on up to main. This is the 'colored functions problem': there are two colors of function, synchronous and asynchronous, and the async color propagates step by step the moment you introduce it somewhere. You can't simply call an async fn from synchronous code — the two worlds don't compose freely. It's the friction promised at the end of №1: color has a price, and it's paid across the whole call chain.

La couleur async remonte, d'appelé en appelantThe async color climbs, from callee to caller
#[tokio::main] async fn mainasync
la racine : le runtimethe root: the runtime
async fn demarrerasync
.await demarrer → doit être async.await demarrer → must be async
async fn lire_configasync
la source : la 1ʳᵉ async fnthe source: the 1st async fn

▲ la couleur remonte : impossible de garder un appelant synchrone au-dessus d'un .await▲ the color climbs: impossible to keep a synchronous caller above an .await

Une seule async fn, et toute la pile suitOne async fn, and the whole stack follows
async fn lire_config() -> Config { /* … .await … */ }

async fn demarrer() -> Config {     // ← DOIT être async : elle .await lire_config
    let cfg = lire_config().await;
    valider(cfg)
}

#[tokio::main]
async fn main() {                   // ← async aussi, remontée jusqu'à la racine
    let cfg = demarrer().await;
}
// la couleur remonte : quiconque .await de l'async DOIT être async lui-même (ou ouvrir un runtime).

Suis la couleur de bas en haut. lire_config est async ; demarrer l'.await, donc demarrer est forcée d'être async ; main l'.await à son tour, donc main est async — et c'est #[tokio::main] qui lui donne le runtime nécessaire. Une seule fonction asynchrone au fond de la pile, et toute la chaîne au-dessus se teinte. Ce n'est pas un défaut d'implémentation : c'est structurel. Le chapitre 3 montrera pourquoi la frontière est réelle, et non un caprice du langage.Follow the color bottom-up. lire_config is async; demarrer .awaits it, so demarrer is forced to be async; main .awaits it in turn, so main is async — and #[tokio::main] gives it the necessary runtime. A single async function at the bottom of the stack, and the whole chain above takes the tint. It's not an implementation flaw: it's structural. Chapter 3 will show why the boundary is real, not a language whim.

Le réflexe du numéroThe issue's reflex

« Où passe la frontière sync / async dans mon code — et qu'est-ce que ça coûte de la franchir ? » L'async n'est pas gratuit : il colore, il contraint, et il fait ressurgir Send dès qu'une tâche traverse des threads. Le bon réflexe n'est pas de tout teindre en async « au cas où », mais de savoir précisément quelle partie du programme est asynchrone, où se trouve le pont avec le monde synchrone, et ce que l'on tient à travers chaque .await.'Where does the sync / async boundary run in my code — and what does crossing it cost?' Async isn't free: it colors, it constrains, and it brings back Send the moment a task crosses threads. The right reflex isn't to tint everything async 'just in case', but to know precisely which part of the program is asynchronous, where the bridge to the synchronous world sits, and what you hold across each .await.

« Function coloring », d'où vient le terme'Function coloring', where the term comes from

L'image vient d'un billet célèbre de Bob Nystrom (« What Color is Your Function? »), écrit à propos de JavaScript. L'idée : dans un langage où l'async est un mode d'appel distinct, chaque fonction porte une « couleur », et les règles d'appel entre couleurs sont asymétriques et pénibles — le rouge ne peut pas appeler le bleu sans cérémonie. Rust hérite de ce problème, syntaxe JS oblige. La différence, c'est qu'en Rust la couleur est honnête : elle est inscrite dans le type (Future), vérifiée à la compilation, jamais silencieuse.The image comes from a famous post by Bob Nystrom ('What Color is Your Function?'), written about JavaScript. The idea: in a language where async is a distinct calling mode, each function carries a 'color', and the calling rules between colors are asymmetric and painful — red can't call blue without ceremony. Rust inherits this problem, JS syntax oblige. The difference is that in Rust the color is honest: it's written in the type (Future), checked at compile time, never silent.

🔒

La suite est réservée The rest is members-only

Le premier numéro est libre. Débloque tout The Rust Loop — tous les volumes, à vie — pour 5 €, paiement unique. The first issue is free. Unlock all of The Rust Loop — every volume, forever — for €5, one-time.

Retour au kiosqueBack to newsstand