ProgramlamaGo Geliştirici

Go'da defer ile return ve paniklerle etkileşimde çalışma nasıl düzenlenmiştir ve defer'de dönen değerleri değiştirmek neden tehlikeli olabilir?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

Tarihsel olarak, defer kavramı Go'da kaynakların güvenli bir şekilde serbest bırakılması ve fonksiyonun sonlandırılması işlemlerinin, fonksiyonun normal veya panic ile bitip bitmediğine bakılmaksızın gerçekleştirilmesi için tanıtılmıştır. Ancak defer'in return ve panic ile etkileşimi, deneyimli geliştiriciler tarafından bile sıklıkla gözden kaçan birkaç sinir bozucu tuzağa sahiptir.

Sorun şudur ki, dönen değerlerin hesaplanma sırası, belirlenmiş dönen değerlerin çalışması ve bu değerlerin defer içinde değiştirilmesi, birçok dilde alışılmış olan davranıştan oldukça farklıdır. Ayrıca, defer içinde hesaplanan değerleri değiştirme girişimi hata verebilir ve beklenmedik davranışlara neden olabilir.

Çözüm — her zaman hatırlamak gerekir: bir fonksiyonun döndürdüğü değerler, defer'in çalıştırılmasından ÖNCE hesaplanır, ancak eğer isimlendirilmiş sonuçlar kullanılıyorsa, bunlar defer içinde gerçek döndürmeden önce değiştirilebilir.

Kod örneği:

func tricky() (res int) { defer func() { res = 42 // Dönen değeri değiştiriyor! }() return 10 } func main() { fmt.Println(tricky()) // 10 değil, 42 yazdıracak }

Anahtar özellikler:

  • defer her zaman return'un argümanları hesaplandıktan sonra, ancak fonksiyondan gerçek dönerken çalıştırılır
  • Defer içinde isimlendirilmiş dönen değerleri değiştirme, dönen değeri etkiler
  • Eğer bir panic gerçekleşirse, tüm defer edilmiş fonksiyonlar recover'e geçmeden veya programdan çıkmadan önce çalıştırılır.

Tuzağa düşürücü sorular.

Defer edilmiş (defer) fonksiyonlar hangi sırayla çalıştırılır?

Bunlar, tanımlandıkları sıranın tersi (stack — LIFO) olarak kesinlikle çalıştırılır.

func f() { defer fmt.Println("1") defer fmt.Println("2") } // Çıktı: 2, sonra 1

Defer fonksiyonlarının parametreleri ne zaman hesaplanır — defer'in tanımlanması anında mı yoksa çalıştırılması anında mı?

Defer fonksiyonu için parametreler defer'in tanımlanması anında hesaplanır, çağrıldığında değil.

func f() { i := 1 defer fmt.Println(i) // değişse bile 1 yazdırılacak i = 2 }

Defer, bir fonksiyonun isimsiz sonucunu değiştirebilir mi?

Hayır. Sadece isimlendirilmiş dönen değerler defer içinde değiştirilebilir.

func f() int { defer func() { /* hiçbir şey değiştiremiyor */ }() return 5 }

Yaygın hatalar ve antipatternlar

  • Defer ile anonim (isimlendirilmemiş) sonucu değiştirme beklentisi
  • Defer ile gerekmedikçe return değerini değiştirmek, beklenmedik davranışlar ve karmaşık hatalara yol açar
  • Defer'de parametrelerin hesaplanması ve iletim sırasını göz ardı etme

Gerçek yaşam örneği

Olumsuz durum

Bir genç geliştirici, defer içinde dönen kodu günlüğe kaydetmek istedi ve hata sonucu isimlendirilmiş dönen değeri değiştirdi, böylece fonksiyonun gerçek sonucunu "silmiş" oldu.

Artıları:

  • Mantık hatasına hızlı düzeltme

Eksileri:

  • Yanlış değer döndürme, zor hata ayıklama

Olumlu durum

Başka bir durumda defer, yalnızca kaynakları serbest bırakmak, günlüğe kaydetmek için kullanıldı ve return'ü değiştirmedi, önemli değerler return'den önce açıkça atandı.

Artıları:

  • Şeffaflık, davranışın tahmin edilebilirliği

Eksileri:

  • Eğer çıkış aşamasında bazı yan etkiler isteniyorsa, ek kod satırları eklemek gerekiyordu.