Go'da kaynakların temizlenmesi veya işlevlerin sona erdirilmesi için ertelemeli çağrılar (defer) ve anonim closure'lar birlikte kullanılır. Bu kalıp, temizleme mantığını gruplandırmaya ve hataları düzgün bir şekilde işlemeye olanak tanır, böylece okunabilir ve güvenilir bir kod sağlar.
Sorunun Tarihi:
Defer, diğer dillerden alınmış ve Go geliştiricisinin hayatını büyük ölçüde kolaylaştırmaktadır. Defer ve closure kullanımı, dosyaların, bağlantıların ve herhangi bir dış kaynağın temizlenmesini sağlamak için bir standart haline gelmiştir; bu, birçok çıkış (return, panic) olabilecek bloklarda uygulanır.
Sorun:
Karmaşık kaynak temizleme mantığında (örneğin, dosyalar, bağlantılar, lokasyonlar) hata durumunda veya fonksiyondan çıkış durumunda temizliğin gerçekleşmesi garanti edilmelidir. Uygun olmayan bir kullanım, bellek sızıntılarına, yanlış temizleme sırasına veya anlamsız hatalara yol açabilir.
Çözüm:
Defer'i anonim bir fonksiyon (closure) ile kullanarak:
Kod örneği:
package main import ( "fmt" "os" ) func WriteFileDemo(filename string) (err error) { f, err := os.Create(filename) if err != nil { return } defer func() { cerr := f.Close() if cerr != nil && err == nil { err = cerr } }() // Dosya ile çalışma mantığı fmt.Fprintln(f, "Merhaba dünya") return // defer burada return olsa bile çalışacaktır } func main() { if err := WriteFileDemo("test.txt"); err != nil { fmt.Println("Hata:", err) } }
Temel Özellikler:
Erteleme closure'ında kullanılan değişkenler ne zaman sabitlenir — defer beyanı sırasında mı yoksa gerçek çağrıda mı?
Defer beyanı sırasında sabitlenir, ancak closure referansla değişkenlere erişirse, defer uygulandığında bu değişkenlerin değerleri kullanılır. Bu bazen beklenmeyen sonuçlara yol açabilir.
for i := 0; i < 3; i++ { defer func() { fmt.Println(i) }() // 2, 2, 2 yazdıracak }
Closure'a parametreler aracılığıyla değerleri geçirmek mümkün mü, böylece referans ile yakalamayı önleyebilir miyiz?
Evet, anonim fonksiyon için parametreler tanımlayabilir ve geçerli değerleri geçirebilirsiniz — böylece değerler kopyalar olarak "dondurulur".
for i := 0; i < 3; i++ { defer func(n int) { fmt.Println(n) }(i) // 2, 1, 0 yazdıracak }
Eğer deferred closure içinde bir panik tespit edilirse ne yapılmalıdır? Onu nasıl işleyebiliriz?
Closure içinde recover() yapısını kullanarak panik durumunun dışarı çıkmasını önleyebilir ve yumuşak bir iyileşme gerçekleştirebilirsiniz.
defer func() { if r := recover(); r != nil { log.Println("Kurtarıldı:", r) } }()
Kod birden fazla dosya açıyor, ancak defer f.Close() yazmayı unutuyor. Hata oluştuğunda kontrolü geri verir ve bazı dosyalar temizlenmeden kalır, kaynak sızıntıları oluşur.
Artılar:
Eksiler:
Tüm satış işlemleri için deferred closure kullanılır: dosya akışını dikkatlice kapatır ve hata işlenir, dosya tamamen yazılmamış olsa bile.
Artılar:
Eksiler: