ProgrammationDéveloppeur Backend

Expliquez les particularités du fonctionnement des paramètres de fonction deferred en Go : quand et comment les paramètres sont-ils calculés pour defer, et en quoi cela diffère-t-il de l'appel de la fonction au moment de l'exécution de defer ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

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 :

  • Les valeurs des paramètres pour les fonctions deferred sont calculées au moment de la déclaration de defer.
  • La fonction deferred s'exécute toujours même en cas de panic (si le panic n'est pas intercepté plus tôt).
  • Si une fonction anonyme est déclarée dans defer, il est possible d'accéder aux valeurs actuelles des variables.

Questions pièges.

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

Erreurs typiques et anti-patrons

  • S'attendre à ce que le paramètre soit valide au moment de l'appel deferred.
  • Utilisation de defer avec des paramètres et des variables mutables.
  • Piles de defer enchevêtrées sans description claire des dépendances.

Exemple de la vie

Cas négatif

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 :

  • Écriture courte si la variable est déjà initialisée.

Inconvénients :

  • Si f == nil — panic.

Cas positif

Enveloppant l'action de nettoyage avec une fonction deferred anonyme avec vérification :

defer func() { if f != nil { f.Close() } }()

Avantages :

  • Sécurité et absence de panics.

Inconvénients :

  • Écriture plus longue et "bruyante".