Vol. 2 · № 02 · Un atelier RustA Rust workshop

L'élision, ou pourquoi 90 % des lifetimes ne s'écrivent pas Elision, or why 90% of lifetimes go unwritten

Les trois règles que le compilateur applique seul pour deviner les durées de vie — et pourquoi on n'annote que lorsqu'il reste une vraie ambiguïté à lever. The three rules the compiler applies on its own to infer lifetimes — and why you annotate only when a genuine ambiguity remains.

AudienceAudience
Dev ayant vu les lifetimes (Vol 2 №1) Dev who has seen lifetimes (Vol 2 №1)
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●○○ LifetimesÉlision
SommaireContents ·
№ 01CadrageFraming3 min

Tu écris déjà des lifetimes. Le compilateur les tait pour toi.You already write lifetimes. The compiler silences them for you.

Le numéro précédent s'est clos sur un paradoxe : si les lifetimes font si peur, pourquoi en croise-t-on si peu dans le vrai code ? Parce que le compilateur en déduit l'immense majorité tout seul — pas par magie, ni par devinette, mais par trois règles parfaitement mécaniques qu'il applique à chaque signature. C'est l'élision. La comprendre, c'est savoir exactement quand le silence suffit, et quand il faut écrire le 'a soi-même.The previous issue closed on a paradox: if lifetimes are so frightening, why do we meet so few in real code? Because the compiler infers the vast majority on its own — not by magic, nor by guessing, but through three perfectly mechanical rules it applies to every signature. That's elision. To understand it is to know exactly when silence suffices, and when you must write the 'a yourself.

La fonction du Vol 1 №3 — sans annotation, et pourtant correcteThe Vol 1 №3 function — unannotated, yet correct
fn premier_mot(s: &str) -> &str {        // aucun 'a, et pourtant ça compile
    s.split(' ').next().unwrap_or("")
}

// Ce que le compilateur lit RÉELLEMENT, en silence :
fn premier_mot<'a>(s: &'a str) -> &'a str { /* ... */ }
Élision ≠ absence de lifetimeElision ≠ absence of lifetime

Le piège mental serait de croire que premier_mot n'a « pas de lifetime ». Faux : la référence rendue en a forcément un. Simplement, le compilateur l'a reconstruit pour toi avant la vérification. L'élision n'efface rien — elle remplit les annotations évidentes pour que tu n'aies pas à les taper. Le contrat de durée est là, exactement comme si tu l'avais écrit.The mental trap would be to think premier_mot has 'no lifetime'. False: the returned reference necessarily has one. The compiler simply reconstructed it for you before checking. Elision erases nothing — it fills in the obvious annotations so you needn't type them. The duration contract is there, exactly as if you'd written it.

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

« Le compilateur peut-il deviner sans ambiguïté à quelle entrée rattacher la sortie ? » Si oui, il élide et tu n'écris rien. S'il reste plusieurs candidats possibles, il s'arrête et réclame le 'a. Connaître les trois règles, c'est prévoir sa réponse avant de compiler.'Can the compiler unambiguously guess which input to tie the output to?' If yes, it elides and you write nothing. If several candidates remain, it stops and demands the 'a. Knowing the three rules means predicting its answer before you compile.

Trois règles, pas une IA.Three rules, not an AI.

L'élision n'est pas de l'inférence intelligente : c'est un algorithme fixe, écrit dans la référence du langage, qui n'examine que la signature — jamais le corps. Trois règles appliquées dans l'ordre. Si, après les trois, une sortie reste sans durée attribuée, c'est une erreur de compilation, pas un pari. Déterministe de bout en bout.Elision isn't clever inference: it's a fixed algorithm, written in the language reference, that examines only the signature — never the body. Three rules applied in order. If, after all three, an output still lacks an assigned duration, it's a compile error, not a gamble. Deterministic end to end.

№ 02La surpriseThe surprise4 min

Pourquoi premier_mot passe, mais plus_long non.Why premier_mot passes, but plus_long doesn't.

Deux fonctions presque jumelles : toutes deux prennent du &str et renvoient du &str. La première compile sans une seule annotation ; la seconde, qu'on a vue au numéro précédent, exige un 'a. La différence n'est pas dans ce qu'elles font — c'est dans le nombre d'entrées empruntées. Là où il n'y a qu'un candidat, l'élision tranche. Là où il y en a deux sans départage, elle se tait et te rend la main.Two near-twin functions: both take &str and return &str. The first compiles without a single annotation; the second, seen last issue, demands a 'a. The difference isn't in what they do — it's in how many borrowed inputs they have. Where there's a single candidate, elision decides. Where there are two with no tie-breaker, it goes silent and hands you back control.

Un seul candidat — le compilateur tranche seulA single candidate — the compiler decides alone
fn premier_mot(s: &str) -> &str { /* ... */ }
// UNE seule entrée empruntée → sa durée va à la sortie. Élidé.

fn entete(&self) -> &str { /* ... */ }
// &self présent → la durée de self va à la sortie. Élidé.
s: &'a str
──→source uniquesingle source
-> &'a str ✓

Dans les deux cas, la sortie n'a qu'une source possible : l'unique paramètre emprunté, ou &self. Aucune ambiguïté à lever — le compilateur attribue la durée et avance. C'est exactement le cas le plus fréquent dans le vrai code : une méthode qui renvoie une vue sur ses propres champs, une fonction qui découpe son unique argument.In both cases, the output has only one possible source: the single borrowed parameter, or &self. No ambiguity to resolve — the compiler assigns the duration and moves on. This is exactly the most common case in real code: a method returning a view into its own fields, a function slicing its single argument.

L'élision ne devine jamais. Quand plusieurs sources sont possibles, elle refuse de choisir à ta place — et te rend la parole.Elision never guesses. When several sources are possible, it refuses to choose for you — and hands you the floor.
№ 03La mécaniqueThe mechanics5 min

Trois règles, appliquées dans l'ordre. Rien d'autre.Three rules, applied in order. Nothing else.

Voici l'algorithme entier, tel qu'il est gravé dans la référence du langage. Le compilateur applique les trois règles dans l'ordre, sur la signature seule. Si, une fois les trois passées, chaque référence en sortie a reçu une durée, l'élision a réussi et tu n'écris rien. S'il en reste une sans durée, c'est E0106 — à toi d'annoter. Tout le mystère des lifetimes tient dans ces trois lignes.Here's the whole algorithm, as carved into the language reference. The compiler applies the three rules in order, on the signature alone. If, once all three have run, every output reference has a duration, elision succeeded and you write nothing. If one is left without, it's E0106 — your turn to annotate. The entire mystery of lifetimes fits in these three lines.

RègleRule 1Chaque paramètre emprunté reçoit sa propre durée, distincte.Each borrowed parameter gets its own distinct lifetime.
fn f(x: &str, y: &str)
↳ fn f<'a, 'b>(x: &'a str, y: &'b str)
RègleRule 2S'il y a exactement une entrée empruntée, sa durée va à toutes les sorties.If there is exactly one borrowed input, its duration goes to every output.
fn f<'a>(x: &'a str) -> &str
↳ fn f<'a>(x: &'a str) -> &'a str
RègleRule 3S'il y a un &self ou &mut self, sa durée va à toutes les sorties.If there is a &self or &mut self, its duration goes to every output.
fn f<'a, 'b>(&'a self, x: &'b str) -> &str
↳ fn f<'a, 'b>(&'a self, x: &'b str) -> &'a str

Règle 1 d'abord (semer les durées), puis 2 et 3 (les router vers la sortie).Rule 1 first (seed the durations), then 2 and 3 (route them to the output).

Pourquoi plus_long échoue, à la lettreWhy plus_long fails, to the letter

Déroulons : Règle 1 donne x: &'a str, y: &'b str — deux durées. Règle 2 ne s'applique pas : il y a deux entrées, pas une. Règle 3 ne s'applique pas : aucun &self. La sortie reste donc sans durée attribuée — l'algorithme s'arrête, E0106. À l'inverse, premier_mot a une seule entrée : la Règle 2 conclut. Et une méthode avec &self : la Règle 3 conclut.Let's unroll it: Rule 1 gives x: &'a str, y: &'b str — two durations. Rule 2 doesn't apply: there are two inputs, not one. Rule 3 doesn't apply: no &self. So the output is left with no duration assigned — the algorithm halts, E0106. Conversely, premier_mot has a single input: Rule 2 concludes. And a method with &self: Rule 3 concludes.

L'élision n'est pas une faveur floue du compilateur : c'est un algorithme à trois règles que tu peux dérouler de tête.Elision isn't a fuzzy favor from the compiler: it's a three-rule algorithm you can unroll in your head.
№ 04Le piègeThe trap4 min

Quand l'élision réussit — mais rattache la sortie à la mauvaise donnée.When elision succeeds — but ties the output to the wrong data.

Le danger n'est pas que l'élision échoue : un échec se voit, c'est E0106, le compilateur te le dit. Le vrai piège, c'est qu'elle réussisse en produisant un contrat que tu n'avais pas en tête. La Règle 3 est la plus traître : dès qu'il y a un &self, toute sortie lui est rattachée — même si, dans le corps, elle emprunte en réalité à un paramètre. L'élision, elle, passe sans broncher ; c'est le corps qui se fait refuser ensuite, sur une contrainte de durée qui semble venir de nulle part.The danger isn't that elision fails: a failure is visible — it's E0106, the compiler tells you. The real trap is when it succeeds, producing a contract you didn't intend. Rule 3 is the most treacherous: the moment there's a &self, every output is tied to it — even if, in the body, it actually borrows from a parameter. Elision itself goes through without a murmur; it's the body that gets rejected afterwards, on a lifetime constraint that seems to come from nowhere.

Le contre-exemple — la Règle 3 lie la sortie à &selfThe counter-example — Rule 3 ties the output to &self
impl Analyseur {
    // On VEUT renvoyer une vue dans `texte`, le paramètre.
    fn extrait(&self, texte: &str) -> &str {   // élidé...
        &texte[self.debut..self.fin]
    }
}
// Règle 3 : la sortie est rattachée à &self, PAS à texte.
// → le corps est refusé, AVANT même le premier appel :
// error: lifetime may not live long enough
//   method was supposed to return data with lifetime `'2`
//   but it is returning data with lifetime `'1`
// help: consider introducing a named lifetime parameter
//       fn extrait<'a>(&self, texte: &'a str) -> &'a str

Le corps renvoie une vue dans texte — mais l'élision, elle, ne lit pas le corps. La Règle 3 voit &self et lui attribue la sortie. Résultat : la signature promet une donnée liée à analyseur quand le corps en rend une liée à texte, et c'est le corps qui est refusé — pour une promesse que tu n'avais jamais formulée. L'erreur parle de durées anonymes, '1 et '2, là où le vrai propriétaire de la donnée est ligne. Le contrat élidé est valide — mais ce n'est pas le tien.The body returns a view into texte — but elision doesn't read the body. Rule 3 sees &self and assigns the output to it. Result: the signature promises data tied to analyseur while the body returns data tied to texte, and it's the body that gets rejected — for a promise you never made. The error talks about anonymous lifetimes, '1 and '2, where the data's real owner is ligne. The elided contract is valid — but it isn't yours.

Le bon réflexe — annoter pour dire la vraie sourceThe right reflex — annotate to state the true source
impl Analyseur {
    // On relie explicitement la sortie au PARAMÈTRE texte
    fn extrait<'t>(&self, texte: &'t str) -> &'t str {
        &texte[self.debut..self.fin]
    }
}
// La sortie vit aussi longtemps que `texte`, indépendamment de l'analyseur.
let vue = analyseur.extrait(&ligne);   // vue suit ligne — comme voulu

On écrit le 't sur texte et sur la sortie : la durée du résultat suit désormais le paramètre, plus l'analyseur. C'est précisément le cas où l'élision ne peut pas deviner juste, parce que la bonne réponse dépend de ce que fait le corps — et qu'elle ne le lit jamais. Annoter n'est pas combattre le compilateur : c'est corriger une hypothèse par défaut qui se trouvait fausse.You write the 't on texte and on the output: the result's duration now follows the parameter, not the analyser. This is precisely the case where elision can't guess right, because the correct answer depends on what the body does — and it never reads it. Annotating isn't fighting the compiler: it's correcting a default assumption that happened to be wrong.

Le signal d'alarmeThe warning sign

Une erreur de durée qui mentionne self alors que tu renvoies une vue dans un paramètre : c'est presque toujours la Règle 3 qui a rattaché la sortie au mauvais endroit. Le remède n'est pas de restructurer la donnée — c'est d'écrire le lifetime du paramètre à la main.A duration error mentioning self when you're returning a view into a parameter: that's almost always Rule 3 tying the output to the wrong place. The remedy isn't to restructure the data — it's to write the parameter's lifetime by hand.

Le test mentalThe mental test

Dès qu'une méthode renvoie une référence et prend à la fois &self ET un paramètre emprunté, demande-toi : « la vue rendue vient-elle de self, ou du paramètre ? » Si c'est du paramètre, l'élision se trompera — annote. Dans tous les autres cas (une seule source possible), laisse-la faire : elle a raison par construction.Whenever a method returns a reference and takes both &self AND a borrowed parameter, ask: 'does the returned view come from self, or from the parameter?' If from the parameter, elision will get it wrong — annotate. In every other case (a single possible source), let it run: it's right by construction.

L'élision qui échoue te prévient. L'élision qui réussit avec le mauvais contrat te laisse seul face à l'erreur — apprends à la reconnaître.Elision that fails warns you. Elision that succeeds with the wrong contract leaves you alone with the error — learn to spot it.
№ 05Bilan & éditoWrap-up & editorial2 min

Le silence du compilateur n'est pas de la magie. C'est un algorithme.The compiler's silence isn't magic. It's an algorithm.

Les lifetimes restent omniprésents — chaque référence en a un. Ce qui disparaît, c'est leur écriture, quand trois règles mécaniques suffisent à les déduire. Une entrée unique, ou un &self : le compilateur conclut seul. Deux sources possibles sans départage : il s'arrête et réclame le 'a. Et quand il conclut là où tu voulais autre chose, c'est à toi d'écrire la vraie relation.Lifetimes stay everywhere — every reference has one. What disappears is the writing of them, when three mechanical rules suffice to infer them. A single input, or a &self: the compiler concludes alone. Two possible sources with no tie-breaker: it stops and demands the 'a. And when it concludes something other than what you meant, it's on you to write the true relationship.

La règleThe ruleCe qu'elle faitWhat it doesLe réflexeThe reflex
Règle 1Rule 1Chaque entrée empruntée reçoit sa propre durée, distincte.Each borrowed input gets its own distinct duration.Sème les durées avant de les router.Seeds the durations before routing them.
Règle 2Rule 2Une seule entrée empruntée → sa durée va à toutes les sorties.A single borrowed input → its duration goes to all outputs.Le cas des fonctions à un argument : élidé d'office.The single-argument function case: elided by default.
Règle 3Rule 3&self présent → SA durée va à toutes les sorties.&self present → ITS duration goes to all outputs.La plus traître : vérifie qu'elle ne lie pas à la mauvaise source.The most treacherous: check it doesn't bind to the wrong source.
SinonOtherwiseSortie sans durée après les trois règles → E0106.Output left without a duration after all three → E0106.Écris le 'a toi-même : l'ambiguïté est réelle.Write the 'a yourself: the ambiguity is real.
Connaître les trois règles, c'est prévoir la réponse du compilateur avant de compiler — et ne plus jamais annoter au hasard.Knowing the three rules means predicting the compiler's answer before you compile — and never annotating at random again.
Mot de l'éditeurFrom the editor

Quand annoter ne suffit plus, ce n'est pas la syntaxe qui manque.When annotating no longer helps, it isn't syntax that's missing.

Il existe un moment, dans l'apprentissage de Rust, où l'on croit avoir compris les lifetimes — et où l'on se met à les empiler. Un 'a ici, un 'b là, un 'a: 'b pour relier les deux, et bientôt une signature illisible que même son auteur ne saurait relire. C'est le signal qu'on a cessé de décrire des durées pour commencer à lutter contre elles. Presque toujours, la cause n'est pas dans les annotations : elle est dans une structure qui essaie de tenir des références là où elle devrait posséder ses données.There's a moment, learning Rust, when you believe you've understood lifetimes — and you start stacking them. A 'a here, a 'b there, an 'a: 'b to link the two, and soon an unreadable signature even its author couldn't reread. That's the sign you've stopped describing durations and begun fighting them. Almost always, the cause isn't in the annotations: it's in a structure trying to hold references where it should own its data.

Prochain numéro : 'static et le combat contre le borrow checker. Pourquoi le réflexe d'annoter « plus fort » jusqu'à coller un 'static partout est une fausse sortie — et comment reconnaître que c'est le design de possession, pas la syntaxe des durées, qu'il faut revoir. La fin du Vol 2, et le vrai message des lifetimes : elles ne sont pas un obstacle, elles sont un diagnostic.Next issue: 'static and the fight against the borrow checker. Why the reflex of annotating 'harder' until you slap a 'static everywhere is a false exit — and how to recognize that it's the ownership design, not duration syntax, that needs rethinking. The close of Vol 2, and the real message of lifetimes: they aren't an obstacle, they're a diagnosis.