Historia koncepcji defer w Go została wprowadzona w celu bezpiecznego zwalniania zasobów i finalizacji działań niezależnie od tego, jak zakończy się wykonanie funkcji (zwykle lub przez panic). Jednak interakcja defer z return i panic ma kilka irytujących pułapek, które często umykają doświadczeniu programistów.
Problem polega na tym, że kolejność obliczania wartości zwracanych, działanie named return values oraz zmiana tych wartości w defer znacznie różni się od zachowania w wielu innych językach. Dodatkowo, błędy mogą się pojawić, gdy w defer próbuje się zmienić już obliczone wartości, powodując nieoczekiwane zachowanie.
Rozwiązanie — zawsze pamiętać: wartości, które zwraca funkcja, są obliczane PRZED uruchomieniem defer, ale jeśli używane są wyniki nazwane, można je zmienić wewnątrz defer przed faktycznym zwrotem z funkcji.
Przykład kodu:
func tricky() (res int) { defer func() { res = 42 // Zmienia wartość zwracaną! }() return 10 } func main() { fmt.Println(tricky()) // Wyświetli 42, a nie 10 }
Kluczowe cechy:
W jakiej kolejności wykonywane są odłożone (defer) funkcje?
Wykonują się ściśle w odwrotnej kolejności ich deklaracji (stos — LIFO).
func f() { defer fmt.Println("1") defer fmt.Println("2") } // Wyświetli: 2, następnie 1
Kiedy dokładnie obliczane są parametry dla funkcji defer — w momencie deklaracji defer czy podczas jej wykonania?
Parametry dla funkcji defer obliczane są w momencie deklaracji defer, a nie przy wywołaniu.
func f() { i := 1 defer fmt.Println(i) // zostanie wyświetlone 1, nawet jeśli i zmieni się później i = 2 }
Czy defer może zmienić nie nazwany wynik funkcji?
Nie. Tylko nazwane wartości zwracane mogą być zmieniane w defer.
func f() int { defer func() { /* nic nie zmieni */ }() return 5 }
Młody programista chciał w defer zalogować kod zwrotu i przez pomyłkę zmienił nazwane wartość zwracaną, w ten sposób "wyczyścił" rzeczywisty wynik funkcji.
Zalety:
Wady:
W innej sytuacji defer był używany tylko do zwalniania zasobów, logowania i nie zmieniał return, a ważne wartości były jawnie przypisane przed return.
Zalety:
Wady: