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

Itérateurs & abstractions zéro-coût Iterators & zero-cost abstractions

La chaîne .iter().map().filter().collect() compile vers la même boucle qu'à la main — lisibilité sans surcoût, le zero-cost prouvé plutôt que promis. The .iter().map().filter().collect() chain compiles to the same loop as by hand — readability without overhead, zero-cost proven rather than promised.

AudienceAudience
Dev venant d'un langage à GC, à l'aise avec les generics (Vol 5) Dev from a GC language, comfortable with generics (Vol 5)
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●●○ CoûtItérateursZero-cost

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

La chaîne d'itérateurs compile vers exactement la boucle qu'on aurait écrite.The iterator chain compiles to exactly the loop you'd have written.

On ouvre le dernier volume, celui du coût. Et la première peur, héritée des langages à GC, vise les itérateurs. En JavaScript ou Python, arr.map().filter() crée un tableau intermédiaire à chaque étape, parcouru en entier : deux allocations, deux passes, pour une seule logique. Le réflexe qu'on en garde est de « redescendre à une boucle manuelle » dès qu'il s'agit de performance. En Rust, cette peur est infondée, et c'est le cœur du numéro. La chaîne .iter().map().filter().collect() ne crée aucune collection intermédiaire et compile, après optimisation, vers le même code machine que la boucle écrite à la main. C'est le sens précis de « zéro-coût » : l'abstraction se paie à la compilation (en monomorphisation et inlining, Vol 5), jamais à l'exécution. Tu écris ce qui est lisible ; tu obtiens ce qui est rapide. Les deux ne s'opposent pas.We open the last volume, the one on cost. And the first fear, inherited from GC languages, targets iterators. In JavaScript or Python, arr.map().filter() creates an intermediate array at each step, traversed in full: two allocations, two passes, for one logic. The reflex you keep is to 'drop down to a manual loop' the moment performance matters. In Rust, that fear is unfounded, and it's the heart of the issue. The .iter().map().filter().collect() chain creates no intermediate collection and compiles, after optimization, to the same machine code as the hand-written loop. That's the precise meaning of 'zero-cost': the abstraction is paid at compile time (in monomorphization and inlining, Vol 5), never at runtime. You write what's readable; you get what's fast. The two aren't opposed.

Deux sources, un seul code machineTwo sources, one machine code
chaîne d'itérateursiterator chain
.iter().filter(..).map(..).collect()
rustcopt + inlineopt + inline
même code machinesame machine code
1 boucle
0 alloc intermédiaire0 intermediate alloc
rustc
boucle à la mainmanual loop
for n in &v { if .. { push(..)} }
La chaîne et la boucle, côte à côteThe chain and the loop, side by side
// la chaîne lisible
let pairs_doubles: Vec<i32> = nombres.iter()
    .filter(|&&n| n % 2 == 0)
    .map(|&n| n * 2)
    .collect();

// la boucle « à la main » qu'on écrirait par peur du surcoût
let mut pairs_doubles = Vec::new();
for &n in &nombres {
    if n % 2 == 0 { pairs_doubles.push(n * 2); }
}
// après optimisation, les deux produisent LE MÊME code machine. la chaîne ne coûte rien
// de plus — c'est ça, le « zéro-coût » : payé à la compilation, pas à l'exécution.

Lis les deux versions : même résultat, même travail. La différence est uniquement à l'écriture — l'une déclare l'intention (filtrer, doubler, collecter), l'autre détaille la mécanique. À la compilation, elles convergent : le compilateur déroule la chaîne en une boucle unique, sans allocation intermédiaire ni appel de fonction résiduel. Choisir la chaîne n'est donc pas un compromis « lisibilité contre vitesse » — c'est les deux à la fois. Le seul vrai compromis serait de réécrire la boucle à la main en croyant gagner quelque chose : on n'y gagne rien, on perd en clarté.Read both versions: same result, same work. The difference is only in the writing — one declares the intent (filter, double, collect), the other spells out the mechanics. At compile time, they converge: the compiler unrolls the chain into a single loop, with no intermediate allocation or residual function call. Choosing the chain isn't a 'readability vs speed' trade-off — it's both at once. The only real trade-off would be rewriting the loop by hand believing you gain something: you gain nothing, you lose clarity.

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

« Cette abstraction a-t-elle un coût à l'exécution, ou seulement à la compilation ? » Pour les itérateurs, la réponse est nette : aucun coût à l'exécution. Le bon réflexe est donc d'écrire la chaîne la plus claire, sans culpabilité de performance, et de ne descendre à la boucle manuelle que si un profil le prouve nécessaire — ce qui, pour les itérateurs, n'arrive presque jamais. C'est la règle de tout le volume : on n'optimise pas sur une intuition, on optimise sur une mesure.'Does this abstraction cost at runtime, or only at compile time?' For iterators, the answer is clear: no runtime cost. The right reflex is thus to write the clearest chain, without performance guilt, and to drop to the manual loop only if a profile proves it necessary — which, for iterators, almost never happens. It's the whole volume's rule: you don't optimize on a hunch, you optimize on a measurement.

« Zéro-coût », la définition de Stroustrup'Zero-cost', Stroustrup's definition

La formule vient du C++ : « ce que tu n'utilises pas, tu ne le paies pas ; et ce que tu utilises, tu ne pourrais pas mieux le coder à la main ». Rust en fait un principe central. Une abstraction zéro-coût n'est pas magiquement gratuite — elle est gratuite parce que le compilateur l'efface : après monomorphisation et inlining, il ne reste aucune trace de la couche d'abstraction dans le binaire. Attention au contre-sens : « zéro-coût » parle du coût à l'exécution, pas du temps de compilation (que la monomorphisation, elle, allonge bel et bien).The phrase comes from C++: 'what you don't use, you don't pay for; and what you do use, you couldn't hand-code better'. Rust makes it a central principle. A zero-cost abstraction isn't magically free — it's free because the compiler erases it: after monomorphization and inlining, no trace of the abstraction layer remains in the binary. Beware the misreading: 'zero-cost' is about runtime cost, not compile time (which monomorphization does lengthen).

🔒

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