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

Lifetimes, ou le temps inscrit dans le type Lifetimes, or time written into the type

L'annotation ne rallonge aucune vie : elle relie des durées pour que le compilateur prouve qu'aucune référence ne survit à sa donnée. On part du use-after-free que Rust refuse, pas de la syntaxe. The annotation lengthens no life: it links durations so the compiler can prove no reference outlives its data. We start from the use-after-free Rust refuses, not from syntax.

AudienceAudience
Dev ayant intégré move, borrowing et slices (Vol 1) Dev who has internalized move, borrowing and slices (Vol 1)
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●○○ LifetimesRéférencesPreuve
SommaireContents ·
№ 01CadrageFraming3 min

Une référence ne doit jamais survivre à sa donnée.A reference must never outlive its data.

Le Vol 1 s'est clos sur une question laissée ouverte : une slice pointe à l'intérieur d'une donnée qu'elle ne possède pas — que se passe-t-il si le propriétaire la libère pendant que la vue regarde encore ? Dans un langage à pointeurs nus, c'est le use-after-free : un pointeur qui désigne de la mémoire déjà rendue. La cause numéro un de failles mémoire. Rust en fait une erreur de compilation. Voici comment il s'y prend.Vol 1 closed on a question left open: a slice points inside data it doesn't own — what happens if the owner frees it while the view is still looking? In a raw-pointer language, that's use-after-free: a pointer designating already-returned memory. The number-one cause of memory vulnerabilities. Rust turns it into a compile error. Here's how.

Le use-after-free que Rust refuse de compilerThe use-after-free Rust refuses to compile
let r;                       // une référence...
{
    let x = 5;               // x naît ici
    r = &x;                  // r emprunte x
}                            // x meurt ICI — sa mémoire est libérée

println!("{r}");             // ✕ r désignerait de la mémoire morte
// error[E0597]: `x` does not live long enough
Ce que voit le compilateurWhat the compiler sees
x
r = &x
temps d'exécution →execution time →

Le compilateur attribue à chaque valeur une durée de vie — un intervalle entre sa naissance et sa libération. La règle est unique : une référence ne peut être utilisée que pendant l'intervalle où sa donnée est vivante. Ici, r prétend vivre plus longtemps que x — la barre dépasse. Refusé.The compiler assigns every value a lifetime — an interval between its birth and its release. The rule is single: a reference may only be used while its data is alive. Here, r claims to live longer than x — the bar overruns. Refused.

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

« Cette référence, de quelle donnée tient-elle sa vie, et cette donnée vit-elle assez longtemps ? » Les lifetimes ne sont pas une syntaxe à mémoriser : ce sont les durées que le compilateur suivait déjà, rendues parfois explicites quand il ne peut plus les déduire seul.'This reference — which data does it draw its life from, and does that data live long enough?' Lifetimes aren't a syntax to memorize: they're the durations the compiler already tracked, made explicit only when it can no longer infer them alone.

Rien de nouveau, en vérité.Nothing new, really.

Tu as déjà rencontré les lifetimes au Vol 1 sans les nommer : le borrow checker qui refusait un emprunt « qui traîne », c'était déjà une question de durée. Ce volume ne fait que donner un nom — et une notation, 'a — à ce que le compilateur calculait en silence. La syntaxe vient en dernier, et seulement quand elle est nécessaire.You already met lifetimes in Vol 1 without naming them: the borrow checker refusing a borrow that 'lingered' was already a question of duration. This volume only gives a name — and a notation, 'a — to what the compiler computed silently. The syntax comes last, and only when it's needed.

№ 02La surpriseThe surprise4 min

Le compilateur refuse — non par sévérité, mais par ignorance.The compiler refuses — not from strictness, but from ignorance.

Une fonction qui renvoie une référence pose une question que sa signature, seule, ne sait pas trancher : la référence rendue pointe-t-elle dans x, ou dans y ? De cette réponse dépend la durée de vie du résultat. Tant qu'elle n'est pas dans la signature, le compilateur ne peut prouver que l'appelant n'obtiendra pas une référence pendante. Il ne devine pas — il exige qu'on relie les durées.A function returning a reference poses a question its signature alone can't settle: does the returned reference point into x, or into y? The result's lifetime depends on that answer. Until it's in the signature, the compiler can't prove the caller won't get a dangling reference. It doesn't guess — it demands you relate the durations.

Ce qu'on écrit naïvementWhat we naively write
fn plus_long(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}
// error[E0106]: missing lifetime specifier
//   --> le type de retour contient une valeur empruntée,
//      mais la signature ne dit pas DE LAQUELLE des deux entrées

Le code semble évident — il l'est, pour un humain. Mais le compilateur raisonne par signatures, pas par corps de fonction : il regarde (&str, &str) -> &str et voit une sortie empruntée sans savoir à quelle entrée rattacher sa durée. Sans ce lien, impossible de garantir à l'appelant que le résultat survivra assez — ni trop.The code looks obvious — it is, to a human. But the compiler reasons by signatures, not by function bodies: it sees (&str, &str) -> &str and finds a borrowed output with no idea which input to tie its duration to. Without that link, it can't guarantee the caller the result will live long enough — nor not too long.

L'annotation de lifetime ne change pas les durées de vie. Elle les relie, pour que le compilateur puisse raisonner sans lire le corps.A lifetime annotation doesn't change lifetimes. It links them, so the compiler can reason without reading the body.
№ 03La mécaniqueThe mechanics5 min

Le résultat vit aussi longtemps que la plus courte des entrées.The result lives as long as the shortest input.

Une fois les trois références reliées par 'a, le compilateur fixe 'a, à chaque appel, à l'intersection des durées : la fenêtre où toutes les entrées marquées 'a sont simultanément vivantes. Le résultat ne peut être utilisé que dans cette fenêtre. C'est pour ça que plus_long n'a pas besoin de savoir laquelle des deux il renverra — il suffit que le résultat ne dépasse aucune des deux.Once the three references are linked by 'a, the compiler sets 'a, at each call, to the intersection of durations: the window where all inputs marked 'a are simultaneously alive. The result may only be used within that window. That's why plus_long needn't know which of the two it returns — it's enough that the result outruns neither.

Sur un site d'appel — 'a se cale sur la plus courteAt a call site — 'a snaps to the shortest
let r;
let longue = String::from("une phrase longue");
{
    let courte = String::from("brève");
    r = plus_long(&longue, &courte);   // 'a = la PLUS COURTE des deux
    println!("{r}");                   // ok ICI : courte est vivante
}                                      // courte meurt — donc 'a finit ici
// println!("{r}");                    // ✕ r ne peut survivre à courte
L'intersection des duréesThe intersection of durations
longue
courte
r ('a)
'a = intersection (borné par courte)'a = intersection (bounded by courte)

longue vit longtemps, courte meurt à la fin du bloc. Comme les deux portent 'a, le compilateur prend la partie commune — bornée par courte. Le résultat r est donc valide dans le bloc, pas après. Peu importe que la valeur renvoyée soit en réalité longue : la signature a promis « pas plus que la plus courte », et le compilateur tient la promesse.longue lives long, courte dies at the block's end. Since both carry 'a, the compiler takes the common part — bounded by courte. So result r is valid inside the block, not after. No matter that the returned value is actually longue: the signature promised 'no longer than the shortest', and the compiler keeps it.

Distinguer les durées : plusieurs paramètres de lifetimeDistinguishing durations: several lifetime parameters

Tout marquer 'a est parfois trop grossier. Si le résultat ne dépend que de x, on l'écrit : fn premier<'a, 'b>(x: &'a str, y: &'b str) -> &'a str. Là, y peut mourir bien avant sans contraindre le résultat — sa durée 'b est indépendante. On relie ce qui est lié, on sépare ce qui ne l'est pas. La signature devient une carte exacte des dépendances de durée.Marking everything 'a is sometimes too coarse. If the result depends only on x, write it so: fn first<'a, 'b>(x: &'a str, y: &'b str) -> &'a str. There, y may die well earlier without constraining the result — its duration 'b is independent. Link what's linked, separate what isn't. The signature becomes an exact map of duration dependencies.

Une signature avec lifetimes n'impose rien au corps : elle documente un contrat de durée que le compilateur vérifie des deux côtés.A signature with lifetimes imposes nothing on the body: it documents a duration contract the compiler checks on both sides.
№ 04Le piègeThe trap4 min

Croire qu'une annotation peut créer de la durée de vie.Believing an annotation can create lifetime.

Le piège jumeau de tout ce volume : devant une erreur de lifetime, ajouter des 'a un peu partout en espérant faire taire le compilateur. Mais une annotation ne fabrique pas de la vie — elle décrit celle qui existe. Si tu renvoies une référence vers une donnée locale, aucune notation au monde ne la fera survivre à la fonction. Le message n'est pas « il manque un 'a » : c'est « cette donnée meurt trop tôt ».The twin trap of this whole volume: facing a lifetime error, sprinkling 'a everywhere hoping to silence the compiler. But an annotation fabricates no life — it describes what exists. If you return a reference to local data, no notation on earth makes it outlive the function. The message isn't 'a 'a is missing': it's 'this data dies too soon'.

Le contre-exemple — annoter pour masquer le vrai problèmeThe counter-example — annotating to mask the real problem
fn etiquette<'a>() -> &'a str {
    let s = String::from("brouillon");
    &s                       // ✕ on renvoie une vue sur s...
}                            // ...mais s meurt ici. 'a ne peut rien y faire.
// error[E0515]: cannot return reference to local variable `s`

Le 'a dans la signature promet « je renvoie une référence valide pour une durée choisie par l'appelant ». Mais le corps emprunte s, une variable locale qui meurt à l'accolade fermante. La promesse est intenable : il n'existe aucune durée où la vue serait encore valide hors de la fonction. Le compilateur ne réclame pas une annotation de plus — il refuse une contradiction.The 'a in the signature promises 'I return a reference valid for a duration the caller picks'. But the body borrows s, a local variable that dies at the closing brace. The promise can't be kept: there's no duration in which the view is still valid outside the function. The compiler isn't asking for one more annotation — it's refusing a contradiction.

Le bon réflexe — si tu produis la donnée, rends-en la possessionThe right reflex — if you produce the data, hand back ownership
fn etiquette() -> String {
    let s = String::from("brouillon");
    s                        // on rend la POSSESSION, pas une vue
}                            // s sort de la fonction vivant, par move

La donnée naît dans la fonction : elle ne peut en sortir que par move, pas par emprunt. On renvoie un String possédé — c'est exactement la convention du Vol 1 №3 : emprunte ce que tu lis, possède ce que tu produis. Pas de lifetime ici, parce qu'il n'y a aucune référence à relier : on transfère une valeur entière.The data is born inside the function: it can leave only by move, not by borrow. You return an owned String — exactly the Vol 1 №3 convention: borrow what you read, own what you produce. No lifetime here, because there's no reference to link: you transfer a whole value.

Lire l'erreur à l'enversRead the error backwards

Une erreur de lifetime ne dit presque jamais « ajoute un 'a ». Elle dit « telle donnée ne vit pas assez longtemps ». La vraie question est donc : qui doit posséder cette donnée pour qu'elle vive aussi longtemps qu'on en a besoin ? Souvent la réponse est « l'appelant » (prends une référence en entrée) ou « le résultat lui-même » (renvoie une valeur possédée).A lifetime error almost never says 'add a 'a'. It says 'this data doesn't live long enough'. So the real question is: who should own this data so it lives as long as we need? Often the answer is 'the caller' (take a reference as input) or 'the result itself' (return an owned value).

Le test mentalThe mental test

Avant d'écrire un 'a : « cette référence pointe-t-elle dans une donnée qui existe déjà avant l'appel ? » Si oui, relie les durées — l'annotation est honnête. Si la donnée naît dans la fonction, aucune annotation ne tient : il faut rendre la possession, ou faire vivre la donnée ailleurs. L'annotation décrit une vie ; elle n'en accorde jamais.Before writing a 'a: 'does this reference point into data that already exists before the call?' If yes, link the durations — the annotation is honest. If the data is born in the function, no annotation holds: hand back ownership, or let the data live elsewhere. The annotation describes a life; it never grants one.

Un 'a n'est pas une incantation pour calmer le compilateur. C'est une promesse que la donnée, elle, doit déjà tenir.A 'a isn't an incantation to placate the compiler. It's a promise the data itself must already keep.
№ 05Bilan & éditoWrap-up & editorial2 min

Le temps n'est pas annoté. Il est prouvé.Time isn't annotated. It's proven.

Une référence ne doit jamais survivre à sa donnée — voilà toute la règle. Quand le compilateur ne peut pas déduire seul quelles durées sont liées, il demande qu'on l'écrive : 'a relie des références pour qu'il puisse raisonner par signatures, sans lire les corps. L'annotation ne crée aucune vie ; elle décrit une relation que la donnée doit déjà honorer.A reference must never outlive its data — that's the whole rule. When the compiler can't infer alone which durations are linked, it asks you to write it: 'a links references so it can reason by signatures, without reading bodies. The annotation creates no life; it describes a relationship the data must already honor.

La notionThe notionCe qu'elle est vraimentWhat it really isLe réflexeThe reflex
LifetimeLifetimeL'intervalle où une donnée est vivante. Le compilateur le suivait déjà au Vol 1.The interval a value is alive. The compiler already tracked it in Vol 1.Penser durées, pas syntaxe.Think durations, not syntax.
'aUne variable de durée qui relie des références — entrées et sortie.A duration variable linking references — inputs and output.Le résultat vit comme la plus courte des entrées marquées 'a.The result lives as long as the shortest input marked 'a.
'a, 'bDurées distinctes : relier ce qui est lié, séparer ce qui ne l'est pas.Distinct durations: link what's linked, separate what isn't.N'oblige pas y à vivre aussi longtemps que x sans raison.Don't force y to live as long as x without reason.
E0515E0515Renvoyer une vue sur une donnée locale : aucune annotation ne sauve.Returning a view of local data: no annotation saves it.Rends la possession (String/Vec), ne masque pas avec 'a.Hand back ownership (String/Vec), don't mask it with 'a.
Une erreur de lifetime ne réclame presque jamais un 'a de plus. Elle dit : cette donnée meurt trop tôt.A lifetime error almost never asks for one more 'a. It says: this data dies too soon.
Mot de l'éditeurFrom the editor

Si les lifetimes font si peur, pourquoi en écrit-on si rarement ?If lifetimes are so scary, why do we write them so rarely?

Les lifetimes ont la réputation d'être le mur où butent les débutants — une forêt de 'a et d'apostrophes. Pourtant, ouvre n'importe quelle base de code Rust mature : tu en croiseras très peu. Le paradoxe se dénoue quand on comprend que le compilateur déduit la grande majorité des durées tout seul, par trois règles mécaniques qu'il applique en silence. On n'écrit un 'a que là où ces règles ne suffisent pas — un cas plus rare qu'on ne le craint.Lifetimes have a reputation as the wall beginners hit — a thicket of 'a and apostrophes. Yet open any mature Rust codebase: you'll meet very few. The paradox dissolves once you grasp that the compiler infers the vast majority of durations on its own, through three mechanical rules it applies silently. You write a 'a only where those rules fall short — a rarer case than the fear suggests.

Prochain numéro : l'élision. Les trois règles exactes qui permettent au compilateur d'omettre les lifetimes — et donc à toi de ne pas les écrire. Comprendre l'élision, c'est transformer les lifetimes de mur en formalité : on saura précisément quand le silence suffit, et quand il faut parler.Next issue: elision. The three exact rules that let the compiler omit lifetimes — and so let you skip writing them. Understanding elision turns lifetimes from a wall into a formality: you'll know precisely when silence suffices, and when you must speak.