Historiquement, le concept de defer a été introduit dans Go pour libérer des ressources en toute sécurité et finaliser des actions indépendamment de la façon dont la fonction se termine (normalement ou en raison d'une panic). Cependant, l'interaction entre defer, return et panic présente plusieurs pièges fâcheux que même les développeurs expérimentés oublient souvent.
Problème : l'ordre de calcul des valeurs de retour, le fonctionnement des valeurs de retour nommées et la modification de ces valeurs dans defer diffèrent fortement du comportement habituel dans de nombreux langages. De plus, des erreurs peuvent survenir si une tentative de modification des valeurs déjà calculées est effectuée dans defer, provoquant un comportement inattendu.
Solution : il est toujours important de se rappeler que les valeurs que renvoie une fonction sont calculées AVANT l'exécution de defer, mais si des résultats nommés sont utilisés, ils peuvent être modifiés à l'intérieur de defer avant le retour effectif de la fonction.
Exemple de code :
func tricky() (res int) { defer func() { res = 42 // Change le valeur de retour ! }() return 10 } func main() { fmt.Println(tricky()) // Affichera 42 au lieu de 10 }
Caractéristiques clés :
Dans quel ordre les fonctions defferées (defer) sont-elles exécutées ?
Elles s'exécutent strictement dans l'ordre inverse de leur déclaration (pile — LIFO).
func f() { defer fmt.Println("1") defer fmt.Println("2") } // Affichera : 2, puis 1
Quand les paramètres des fonctions defer sont-ils calculés — au moment de la déclaration de defer ou lors de son exécution ?
Les paramètres pour la fonction defer sont calculés au moment de la déclaration de defer, et non lors de l'appel.
func f() { i := 1 defer fmt.Println(i) // affichera 1, même si i change plus tard i = 2 }
Le defer peut-il modifier un résultat non nommé d'une fonction ?
Non. Seules les valeurs de retour nommées peuvent être modifiées dans defer.
func f() int { defer func() { /* ne rien changer */ }() return 5 }
Un jeune développeur voulait enregistrer le code de retour dans defer et a par erreur modifié une valeur de retour nommée, ce qui a "effacé" le résultat réel de la fonction.
Avantages :
Inconvénients :
Dans une autre situation, defer a été utilisé uniquement pour libérer des ressources, enregistrer et n'a pas modifié le retour, tandis que les valeurs importantes ont été explicitement affectées avant le return.
Avantages :
Inconvénients :