Defer, mevcut işlev tamamlandıktan sonra, hatta panic meydana gelse bile bir işlevin çalıştırılmasını sağlayan benzersiz bir Go mekanizmasıdır. Tarihsel olarak, bu, on-exit yapılarının benzeri bir yapı ancak Go'da deferred çağrılarının bir yığını olarak uygulanmıştır. Önemli bir ayrıntı — deferred işlevin parametreleri defer'in beyan edilmesi anında hesaplanır, gerçek çağrısı sırasında değil!
Sorun — belirgin olmayan davranış: parametrelerin defer tetiklendiğinde geçirileceğini beklemek yaygındır, ancak öyle değildir. Bu genellikle değişkenlerle veya dış değişkenlerle çalışırken hatalara yol açar.
Çözüm — parametrelerin hemen hesaplandığını ve deferred çağrısının etkisinin sonradan meydana geleceğini her zaman göz önünde bulundurmak.
Kod örneği:
func f() { x := 5 defer fmt.Println(x) x = 10 } // Çıktı: 5, değil 10
Anahtar özellikler:
Aşağıdaki kod neyi yazdırır? Neden?
func main() { i := 0 defer fmt.Println(i) i = 1 }
Cevap: 0 yazdırır. fmt.Println işlevinin argümanı defer beyan edildiği anda saklanır.
Defer beyanından sonra değişkenin değiştirilmesi, değerin işleve aktarımını etkiler mi?
Hayır, etkilemez — hesaplama defer beyan edildiği anda gerçekleşir:
defer fmt.Println(x) // x'in değeri şu anda saklanır, sonra değil
Son durumda değişkeni nihai olarak yazdıracak bir defer yapabilir miyiz?
Evet, anonim bir işlev (closure) kullanarak:
defer func() { fmt.Println(x) }() // deferred çağrısı sırasında x'in güncel değerini alır
Kod şu şekilde çağrılır:
var f *os.File // ... defer f.Close()
Ama f daha sonra atanır, bu nedenle nil işaretçisini çağırmaktan panic çıkacaktır!
Artılar:
Eksiler:
Kontrol ile bir anonim deferred işlev aracılığıyla temizleme eylemini sarmak:
defer func() { if f != nil { f.Close() } }()
Artılar:
Eksiler: