Defer est un mécanisme unique de Go qui permet d'exécuter une fonction après la fin de la fonction actuelle, même en cas de panic. Historiquement, cela est l'équivalent des constructions on-exit, mais en Go, cela est réalisé comme une pile d'appels deferred. Un point important est que les paramètres de la fonction deferred sont calculés immédiatement au moment de la déclaration de defer, et non au moment de son véritable appel !
Problème — comportement non évident : on peut s'attendre à ce que les paramètres soient transmis lors de l'exécution de defer, mais ce n'est pas le cas. Cela conduit souvent à des bogues lorsque l'on travaille avec des variables mutables ou externes.
Solution — avoir toujours à l'esprit que les paramètres sont calculés immédiatement, et que l'effet de l'appel deferred se produit ensuite.
Exemple de code :
func f() { x := 5 defer fmt.Println(x) x = 10 } // Affichera : 5, et non 10
Caractéristiques clés :
Que renverra le code suivant ? Pourquoi ?
func main() { i := 0 defer fmt.Println(i) i = 1 }
Réponse : affichera 0. L'argument de la fonction fmt.Println est sauvegardé immédiatement lors de la déclaration de defer.
Le changement de variable après la déclaration de defer affecte-t-il le transfert de sa valeur à la fonction ?
Non, cela n'affecte pas – le calcul se produit lors de la déclaration de defer :
defer fmt.Println(x) // La valeur x est sauvegardée maintenant, pas plus tard
Peut-on faire defer pour afficher finalement le dernier état de la variable ?
Oui, en utilisant une fonction anonyme (closure) :
defer func() { fmt.Println(x) }() // capturera la valeur actuelle de x au moment de l'appel deferred
Le code est appelé comme ceci :
var f *os.File // ... defer f.Close()
Mais f est assigné plus tard, donc panic due à l'appel d'un pointeur nil !
Avantages :
Inconvénients :
Enveloppant l'action de nettoyage avec une fonction deferred anonyme avec vérification :
defer func() { if f != nil { f.Close() } }()
Avantages :
Inconvénients :