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

defer, la promesse différée defer, the deferred promise

defer planifie du code pour la sortie de fonction, quelle que soit la sortie — en pile LIFO. On libère une ressource juste à côté de son acquisition. Reste un piège : l'erreur de defer f.Close() qu'on laisse filer sans la regarder. defer schedules code for the function's exit, whatever the exit — LIFO order. You free a resource right next to where you acquired it. One trap remains: the error from defer f.Close() that slips away unlooked-at.

AudienceAudience
Dev qui ferme ses ressources à la main sur chaque return, ou ignore l'erreur de Close en écriture Dev who closes resources by hand on every return, or ignores Close's error on a write
Format
Self-paced
ChapitresChapters
5
Date
Déc 2027 Dec 2027
≈ 15 min ●○○○ deferRessources

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

defer planifie du code pour la sortie de la fonction — quelle que soit la façon dont elle sort.defer schedules code for the function's exit — whatever way it exits.

On ouvre un volume nouveau. Jusqu'ici, on a parlé d'espace — où vit une valeur, qui la possède, qui peut la changer. defer parle de temps : il déplace un bout de code dans le futur, à l'instant précis où la fonction se terminera. Sa promesse est simple et puissante : tu écris defer suivi d'un appel, et cet appel ne s'exécute pas tout de suite — il est mis de côté, et Go garantit de le lancer juste avant que la fonction ne rende la main, quel que soit le chemin emprunté pour sortir. Un return en début de fonction, un return après une erreur, un return tout au fond, et même un panic qui déroule la pile : dans tous les cas, le code différé s'exécute. C'est exactement ce qu'il faut pour gérer les ressources. Une ressource qu'on acquiert — un fichier ouvert, un verrou pris, une connexion établie — doit toujours être libérée, et le drame habituel est d'oublier cette libération sur l'un des nombreux chemins de sortie d'une fonction. defer supprime le problème à la racine : on écrit la libération juste après l'acquisition, sur la ligne d'à côté, et on n'y pense plus. Acquérir puis defer libérer, collés l'un à l'autre, est l'un des motifs les plus reconnaissables du Go idiomatique. Le code différé est lisible là où il compte — près de ce qu'il nettoie — et il s'exécute là où il faut — à la toute fin, sans exception.We open a new volume. So far, we've spoken of space — where a value lives, who owns it, who can change it. defer speaks of time: it moves a piece of code into the future, to the precise instant the function ends. Its promise is simple and powerful: you write defer followed by a call, and that call doesn't run right away — it's set aside, and Go guarantees to launch it just before the function returns, whatever path was taken to exit. A return early in the function, a return after an error, a return deep at the bottom, and even a panic unwinding the stack: in every case, the deferred code runs. That's exactly what's needed to manage resources. A resource you acquire — an open file, a held lock, an established connection — must always be released, and the usual tragedy is forgetting that release on one of a function's many exit paths. defer removes the problem at the root: you write the release right after the acquisition, on the next line, and you stop thinking about it. Acquire then defer release, glued together, is one of the most recognizable patterns of idiomatic Go. The deferred code is readable where it matters — near what it cleans up — and it runs where it must — at the very end, no exception.

Sans defer : fermer sur chaque chemin, et en oublier unWithout defer: close on every path, and miss one
func process(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }

    data, err := parse(f)
    if err != nil {
        f.Close()              // … qu'il faut penser à fermer ici …
        return err
    }
    if data.empty() {
        f.Close()              // … et ici …
        return ErrEmpty
    }
    f.Close()                  // … et ici. Un seul oubli, et le fichier fuit.
    return nil
}
Avec defer : une libération, collée à l'acquisitionWith defer: one release, glued to the acquisition
func process(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close()            // planifié MAINTENANT, exécuté à la SORTIE

    data, err := parse(f)
    if err != nil {
        return err             // f.Close() aura lieu, automatiquement
    }
    if data.empty() {
        return ErrEmpty        // … ici aussi …
    }
    return nil                 // … et ici. Un seul Close, pour tous les chemins.
}
Tous les chemins de sortie passent par le deferEvery exit path goes through the defer
return tôt
return sur erreur
return final
panic qui déroule
defer f.Close()
s'exécute, toujours

Peu importe par où la fonction s'en va : le code différé est sur le passage. Une seule ligne couvre tous les retours, présents et ceux qu'un collègue ajoutera demain — y compris la sortie par panic.No matter where the function leaves: the deferred code is on the way out. A single line covers every return, present ones and those a colleague adds tomorrow — including the exit by panic.

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

« J'acquiers une ressource ? la ligne suivante est son defer de libération. » Le geste se fait en deux temps qu'on n'écrit qu'une fois : f, _ := os.Open(...) puis defer f.Close(), l'un sous l'autre. La proximité est le bénéfice principal : le lecteur voit, au même endroit, ce qui est pris et ce qui sera rendu. Plus de fermeture éparpillée sur dix return, plus de fuite quand un chemin de sortie est ajouté plus tard."Do I acquire a resource? the next line is its defer release." The gesture happens in two beats you write only once: f, _ := os.Open(...) then defer f.Close(), one under the other. The proximity is the main benefit: the reader sees, in one place, what's taken and what will be returned. No more close scattered across ten returns, no more leak when an exit path is added later.

defer n'est pas « à la fin du bloc »defer isn't 'at the end of the block'

Un piège de débutant venu d'autres langages : croire que defer s'exécute à la fin du bloc courant — la fin du if, la fin du for. Faux. defer est attaché à la fonction, pas au bloc. Un defer écrit à l'intérieur d'un if ne se déclenchera pas en sortant du if, mais en sortant de la fonction tout entière. Cette distinction est sans conséquence dans une fonction courte, mais elle devient un vrai problème dans une boucle : un defer dans un for s'accumule et n'est honoré qu'au retour de la fonction, pas à chaque tour. On y reviendra — c'est l'un des pièges du numéro 2 — mais retiens dès maintenant la portée exacte : defer vise la sortie de la fonction, et rien d'autre.A beginner trap coming from other languages: believing defer runs at the end of the current block — the end of the if, the end of the for. Wrong. defer is attached to the function, not the block. A defer written inside an if won't fire when leaving the if, but when leaving the whole function. This distinction is harmless in a short function, but it becomes a real problem in a loop: a defer in a for accumulates and is only honored on the function's return, not each iteration. We'll come back to it — it's one of issue 2's traps — but remember right now the exact scope: defer targets the function's exit, and nothing else.

🔒

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