Vol. 7 — № 02
The Go Loop · KiosqueNewsstand
Blog  
Un atelier Go · Édition d'ApprentissageA Go Workshop · Learning Edition

La capture du defer What defer captures

Les arguments d'un defer sont évalués à l'instant du defer ; son corps, lui, s'exécute à la sortie. Toute la subtilité — et le bug de la variable de boucle, et le defer qui s'accumule dans une boucle — tient dans cet écart. A defer's arguments are evaluated at the moment of defer; its body runs at exit. All the subtlety — the loop-variable bug, the defer piling up inside a loop — lives in that gap.

AudienceAudience
Dev mordu par un defer qui capture la mauvaise valeur, ou qui accumule ses Close dans une boucle Dev bitten by a defer that captures the wrong value, or piles up its Closes in a loop
Format
Self-paced
ChapitresChapters
5
Date
Jan 2028 Jan 2028
≈ 16 min ●●●○ deferCapturePiège

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

Un defer vit en deux temps : ses arguments sont évalués maintenant, son appel s'exécute à la sortie.A defer lives in two times: its arguments are evaluated now, its call runs at exit.

Le numéro précédent a posé le quand de defer : à la sortie de la fonction, quel que soit le chemin. Ce numéro précise le quoi — qu'est-ce, exactement, qui est mis de côté ? Et la réponse est plus subtile qu'il n'y paraît, parce que defer fonctionne en deux temps distincts. Quand l'exécution rencontre la ligne defer, deux choses se produisent à des instants différents. Premièrement, sur-le-champ, Go évalue les arguments de l'appel différé. Si tu écris defer log(x), la valeur de x est lue et figée à cet instant précis, même si l'appel à log n'aura lieu que bien plus tard. Deuxièmement, à la sortie de la fonction, Go exécute enfin l'appel, avec les arguments qu'il avait gelés. Cet écart — arguments évalués tôt, appel exécuté tard — est invisible tant que les variables concernées ne changent pas entre les deux moments. Mais dès qu'elles changent, le comportement surprend : le defer utilise la photographie prise à l'instant du defer, pas l'état final. C'est une décision de conception délibérée, et plutôt sensée : elle capture l'intention du moment où tu écris le defer. Mais elle a une exception majeure, qui est la source de presque tous les pièges du numéro : si l'appel différé est une fermeture sans arguments, alors il n'y a rien à évaluer tôt, et le corps de la fermeture lira les variables à la sortie, dans leur état final. Appel direct avec arguments, ou fermeture : ces deux formes capturent à deux moments opposés. Tout le numéro vit dans cet écart.The previous issue laid down defer's when: at the function's exit, whatever the path. This issue clarifies the what — what, exactly, is set aside? And the answer is subtler than it seems, because defer works in two distinct times. When execution meets the defer line, two things happen at different instants. First, on the spot, Go evaluates the deferred call's arguments. If you write defer log(x), the value of x is read and frozen at that precise instant, even though the call to log will only happen much later. Second, at the function's exit, Go finally runs the call, with the arguments it had frozen. This gap — arguments evaluated early, call run late — is invisible as long as the variables involved don't change between the two moments. But the moment they change, the behavior surprises: the defer uses the photograph taken at the instant of defer, not the final state. It's a deliberate design decision, and a rather sensible one: it captures the intent of the moment you write the defer. But it has a major exception, the source of nearly all the issue's traps: if the deferred call is a closure with no arguments, then there's nothing to evaluate early, and the closure's body will read the variables at exit, in their final state. Direct call with arguments, or closure: these two forms capture at two opposite moments. The whole issue lives in that gap.

L'argument est gelé à l'instant du defer, pas à la sortieThe argument is frozen at the instant of defer, not at exit
func demo() {
    x := 1
    defer fmt.Println("différé :", x)   // x est ÉVALUÉ ICI → fige la valeur 1
    x = 2
    fmt.Println("immédiat :", x)        // 2
}
// sortie :
//   immédiat : 2
//   différé : 1     ← l'argument a été figé au moment du defer, pas à la sortie
Deux instants, un seul deferTwo instants, one defer
à l'instant du defer
les arguments sont évalués et figés
· · · · · ·
à la sortie de la fonction
l'appel différé s'exécute, avec ces arguments gelés

Entre les deux instants, la fonction continue et les variables changent. Un appel différé defer f(x) a déjà figé x ; une fermeture différée, elle, lira x à la sortie. Savoir laquelle tu écris, c'est savoir ce que tu vas obtenir.Between the two instants, the function continues and variables change. A deferred call defer f(x) has already frozen x; a deferred closure will read x at exit. Knowing which you write is knowing what you'll get.

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

« Mon defer a-t-il des arguments, ou est-ce une fermeture ? Le premier fige maintenant, la seconde lit à la sortie. » Cette seule question dissipe la quasi-totalité des surprises. Tu choisis le moment de capture en choisissant la forme : passe une valeur en argument pour la figer tout de suite, mets-la dans le corps d'une fermeture pour la lire à la fin. Les deux sont utiles — encore faut-il savoir laquelle on a sous les doigts."Does my defer have arguments, or is it a closure? The first freezes now, the second reads at exit." This single question dispels nearly all the surprises. You choose the moment of capture by choosing the form: pass a value as an argument to freeze it at once, put it in a closure's body to read it at the end. Both are useful — you just need to know which one you have under your fingers.

Pourquoi geler les arguments tôt ?Why freeze the arguments early?

Le choix de Go — évaluer les arguments à l'instant du defer — n'est pas arbitraire. Il capture l'état du monde au moment où tu décides de différer, ce qui est souvent exactement ce qu'on veut. Un defer trace(time.Now()) en début de fonction fige l'heure de début, pas l'heure de fin : précisément l'usage attendu. Un defer log(currentStep) enregistre l'étape où l'on était quand on a posé le defer. En gelant les arguments, Go te permet de capturer un instantané sans avoir à le stocker toi-même dans une variable temporaire. La fermeture offre le comportement inverse — lire l'état final — pour les cas où c'est ce qu'on veut. Deux outils, deux moments de capture, et la responsabilité de choisir. Le langage ne devine pas ton intention ; il te donne deux formes claires et te laisse l'exprimer.Go's choice — evaluating arguments at the instant of defer — isn't arbitrary. It captures the state of the world at the moment you decide to defer, which is often exactly what you want. A defer trace(time.Now()) at a function's start freezes the start time, not the end time: precisely the expected use. A defer log(currentStep) records the step you were at when you placed the defer. By freezing the arguments, Go lets you capture a snapshot without having to store it yourself in a temporary variable. The closure offers the opposite behavior — read the final state — for cases where that's what you want. Two tools, two moments of capture, and the responsibility to choose. The language doesn't guess your intent; it gives you two clear forms and lets you express it.

🔒

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

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

Retour au kiosqueBack to newsstand