programowanieProgramista Go

Wyjaśnij, jak działa defer w Go i kiedy kolejność wywołań może być zaskoczeniem. Jakie pułapki spotykane są w praktyce?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Słowo kluczowe defer w Go odkłada wykonanie wskazanych funkcji do momentu wyjścia z otaczającej funkcji — funkcje są gromadzone na stosie i wykonywane w odwrotnej kolejności (LIFO). Często wykorzystuje się to do zwalniania zasobów (pliki, mutexy, połączenia).

Cechą szczególną jest to, że wszystkie argumenty funkcji przekazanych do defer są obliczane natychmiast w momencie deklaracji, a nie podczas wykonania.

func test() { for i := 0; i < 3; i++ { defer fmt.Println(i) // Wydrukuje: 2, 1, 0 } }

Pytanie podchwytliwe.

Co wyniknie z poniższego kodu?

func main() { for i := 0; i < 3; i++ { defer fmt.Println(i) } }

Odpowiedź:

To wydrukuje:

2
1
0

Ponieważ w każdym cyklu argument i jest obliczany natychmiast (w momencie defer), a wszystkie wartości są przechowywane na stosie defer.

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu.


Historia

W serwisie plikowym zapomniano uwzględnić, że defer jest wywoływane tylko przy normalnym wyjściu z funkcji. Pojawiły się wycieki, gdy program awaryjnie kończył się przed punktem wywołania defer.


Historia

W potoku danych nastąpiło zapomnienie — w pętli używano defer do zamykania połączeń, ale faktycznie zamknęły się one dopiero po zakończeniu całej funkcji, a nie po każdej iteracji. Doprowadziło to do wyczerpania zasobów.


Historia

W loggerze używano defer z funkcją anonimową, oczekując, że argument zostanie obliczony w momencie wywołania. Z tego powodu log na końcu zawierał nieaktualne informacje, ponieważ wartości były przechwytywane wcześniej.