Go, program çalıştırıldığında paketlerin, değişkenlerin ve fonksiyonların başlatılması ile ilgili katı kurallara sahiptir. Temel mekanizma, init fonksiyonlarının yürütülmesi ve global değişkenlerin başlatılmasıdır. Bu süreçlerin doğru anlaşılması, hataların ve beklenmedik etkilerin önlenmesi açısından önemlidir.
Konu Tarihçesi:
Go, başlangıçtan itibaren başlatma aşamalarını kesin bir şekilde ayırmıştır: beyan, başlatma ve ardından kod yürütme. C/C++ gibi dillerde genellikle global değişkenlerin kurucuları kullanılırken, Go'da başlatma sırası belirli olmasına rağmen bazı nüanslar vardır.
Sorun:
Global değişkenlerin başlatılması veya init çağrısı, paketler arasında karşılıklı veya döngüsel durumlarla sonuçlanabilir, bu da tuzağa düşmek için kolay bir yoldur. Bu durumları izlemek zordur ve programlar, geliştiricinin beklediğinden farklı davranabilir, özellikle gizli bağımlılıklar veya başlatma sırasında state'in gizlenmesi durumlarında.
Çözüm:
Go'daki paketler, bağımlılıklarına göre başlatılır: önce bağımlılıklar, sonra mevcut paket. İlk olarak, package-level değişkenler (kaynak dosyasındaki sıralarına göre) başlatılır, ardından herhangi bir init() fonksiyonu çağrılır, eğer varsa. Bir dosyada birden fazla init() tanımlanabilir. Bir paketin dosyaları arasındaki başlatma sırası belirlenmemiştir (bu hatalara yol açabilir).
Kod örneği:
// a.go package main import "fmt" func init() { fmt.Println("a.go'dan init") } // b.go package main import "fmt" func init() { fmt.Println("b.go'dan init") }
Bu init fonksiyonlarının yürütülme sırası, aynı dizindeki dosyalar arasında öngörülemez, ancak her zaman main() fonksiyonundan önce gerçekleşir.
Önemli özellikler:
Farklı dosyalardaki init fonksiyonlarının belirli bir sıra ile yürütülmesine güvenilir mi?
Hayır! Go, bir paketteki farklı dosyalar arasındaki init fonksiyonları için bir sıralama garantisi vermez. Belirli bir sıraya güvenmek, zor tespit edilen hatalarla ve iş mantığının bozulmasıyla sonuçlanabilir.
Global değişkenler, init fonksiyonu çalıştırılmadan önce başlatılmamış olabilir mi?
Hayır — paketin tüm global değişkenleri, bu paketin tüm init fonksiyonlarından önce kesin bir sıralama ile tanımlanır. Tek istisna, paketler arasındaki çapraz başlatmalardır (aşağıya bakınız).
Paketler arasındaki init döngüsel bağımlılıkları nasıl önleyebilirim?
Go, paketler düzeyinde döngüsel ithalatlara izin vermez (bu bir derleme zamanı hatasıdır), ancak dolaylı başlatma tuzağına düşebilirsiniz: A, B'den, B, C'den bağımlı olabilir ve C (bir global değişken veya init aracılığıyla) A'dan kod çağırabilir. Bu gibi durumlarda, init/global kurucularının çağrılma sırası belirsiz hale gelebilir.
Servislerin başlatma mantığı, farklı dosyalardaki birkaç init fonksiyonu içinde gerçekleştirilmiştir. Bir init, diğerinin sonucuna bağımlıdır, bu da farklı derlemeler ve farklı sunucularda rastgele davranışlara yol açar.
Artıları:
Eksileri:
Tüm state ve başlatma, main() içinde belirgin çağrılarla gerçekleştirilir. init fonksiyonları yalnızca başlatma izleme ve küçük kontroller için kullanılır.
Artıları:
Eksileri: