Teknik materyaller
Go programları, programı hemen çıkmak için os.Exit
veya log.Fatal*
kullanır (panic
kullanmak programdan çıkmak için iyi bir yol değildir, lütfen panic kullanmayın).
Sadece main()
içinde os.Exit
veya log.Fatal*
bir kez çağrılmalıdır. Diğer tüm işlevler hata durumlarını çağıran kodun geriye dönmesi gerekmektedir.
Tavsiye edilmez:
func main() {
body := readFile(path)
fmt.Println(body)
}
func readFile(path string) string {
f, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
b, err := os.ReadAll(f)
if err != nil {
log.Fatal(err)
}
return string(b)
}
Tavsiye edilen:
func main() {
body, err := readFile(path)
if err != nil {
log.Fatal(err)
}
fmt.Println(body)
}
func readFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
b, err := os.ReadAll(f)
if err != nil {
return "", err
}
return string(b), nil
}
İlke olarak, birden fazla çıkış noktası olan programlar birkaç soruna neden olur:
- Belirsiz kontrol akışı: Herhangi bir işlev programdan çıkabilir, bu da kontrol akışını anlamayı zorlaştırır.
- Test etmesi zor: Programdan çıkan işlevler aynı zamanda onları çağıran testleri de sonlandırır. Bu, işlevleri test etmeyi zorlaştırır ve henüz çalıştırılmamış diğer testleri atlamak riskini artırır.
- Temizliği atlamak: Bir işlev programdan çıktığında, ertelemeli işlev çağrılarını atlar. Bu, önemli temizlik görevlerini atlamak riskini artırır.
Bir defalık çıkış
Mümkünse, main()
işlevinizde yalnızca maksimum bir kez os.Exit
veya log.Fatal
çağrısı olmalıdır. Programın çalışmasını durduran birden fazla hata senaryosu varsa, bu mantığı ayrı bir işlevde yerleştirin ve hatayı oradan geri döndürün. Bu, main()
işlevini kısaltacak ve tüm kritik iş mantığını ayrı, test edilebilir bir işlevde yerleştirecektir.
Tavsiye edilmez yaklaşım:
package main
func main() {
args := os.Args[1:]
if len(args) != 1 {
log.Fatal("dosya eksik")
}
name := args[0]
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Eğer bu satırdan sonra log.Fatal çağırırsak
// f.Close çalıştırılacak.
b, err := os.ReadAll(f)
if err != nil {
log.Fatal(err)
}
// ...
}
Tavsiye edilen yaklaşım:
package main
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
args := os.Args[1:]
if len(args) != 1 {
return errors.New("dosya eksik")
}
name := args[0]
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
b, err := os.ReadAll(f)
if err != nil {
return err
}
// ...
}
Yukarıdaki örnek log.Fatal
kullanıyor, ancak bu kılavuz os.Exit
veya os.Exit
çağrısı yapan herhangi bir kütüphane kodu için de geçerlidir.
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
İhtiyaca göre run()
'un imzasını değiştirebilirsiniz. Örneğin, programınızın belirli bir hata koduyla çıkması gerekiyorsa, run()
hata yerine çıkış kodunu geri döndürebilir. Bu ayrıca birim testlerin bu davranışı doğrudan doğrulamasına izin verir.
func main() {
os.Exit(run(args))
}
func run() (exitCode int) {
// ...
}
Lütfen bu örneklerde kullanılan run()
işlevinin zorunlu olmadığını unutmayın. İsim, imza ve run()
işlevinin kurulumu esnektir. Diğer şeylerin yanı sıra şunları yapabilirsiniz:
- Ayrıştırılmamış komut satırı argümanlarını kabul etmek (örn.
run(os.Args[1:])
) - Komut satırı argümanlarını
main()
içinde ayrıştırmak verun
'a iletmek - Çıkış kodunu özel bir hata türü ile
main()
'e geri döndürmek - İş mantığını farklı bir soyutlama seviyesine yerleştirmek
package main
Bu kılavuz, main()
'in gerçek çıkış akışından sorumlu bir yerin olmasını gerektirir.