Modül dersleri (2/5)
Deyimsel hata işleme
Go'da hata işleme açıktır: istisna yok, deneme/yakalama yok. Başarısız olabilecek her işlev, son değeri olarak error değerini döndürür ve çağıran karar verir. Bu bölüm, hataları sağlam bir şekilde üretmek, yaymak ve denetlemek için kullanılan deyimsel kalıpları toplar.
Altın kural
data, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("config %s: %w", path, err)
}Dört prensip:
errorson dönüştür.if err != nilile hemen kontrol edin.- Yayılmadan önce bağlam ekleyin (
fmt.Errorf+%wile). - Asla bir hatayı göz ardı etmeyin: ya sorunu halledin, günlüğe kaydedin ya da iade edin.
_ = file.Close() // EXPLICITLY ignored (you know what you're doing)Nöbetçi hataları
nöbetçi hatası, arayan kişinin karşılaştırabileceği, dışa aktarılmış bir genel değişken olan var ErrXxx = errors.New("...")'dir:
package store
var ErrNotFound = errors.New("not found")
func Get(key string) (string, error) {
if !exists(key) {
return "", ErrNotFound
}
// ...
}Arayan, sarma zincirinde yürüyen errors.Is (== DEĞİL) kullanır:
v, err := store.Get(k)
if errors.Is(err, store.ErrNotFound) {
// handle 404
}Standart örnekler: io.EOF, sql.ErrNoRows, os.ErrNotExist.
Özel yazılan hatalar
Yapılandırılmış verileri (HTTP kodu, geçersiz alan vb.) taşımak için bir tür tanımlayın:
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Msg)
}Arayan kişi zincir boyunca güvenli bir şekilde yayın yapmak için errors.As kullanır:
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("invalid field:", ve.Field)
}errors.As, aktarılan işaretçiye atanabilecek bir hata bulana kadar Unwrap() zincirinde yürür.
%w ile sarmalama
fmt.Errorf ve %w fiili, orijinali saran ve onu errors.Is/errors.As için koruyan bir hata üretir:
if err := db.Query(); err != nil {
return fmt.Errorf("load users: %w", err)
}%w olmadan (%v veya %s kullanarak) daha zengin bir dize elde edersiniz ancak zinciri kaybedersiniz: arayan kişi artık belirli hataları tanıyamaz.
%w için kurallar:
- Her
fmt.Errorfçağrı için yalnızca bir tane (çoklu sarmaerrors.Joingerektirir). - Bağımsız değişken bir dize değil, bir
errorOLMALIDIR. - bağlam ekleyin, orijinal mesajı kopyalamayın.
// bad: duplicates
return fmt.Errorf("error: %w", err) // "error: file not found"
// good: useful context
return fmt.Errorf("load config %s: %w", path, err)Birden fazla hata için errors.Join
Go 1.20'den bu yana, errors.Join(errs...) birden fazla hatayı tek bir hatada birleştirerek hepsinin errors.Is/As tarafından tespit edilmesini sağlar:
var errs []error
for _, item := range items {
if err := process(item); err != nil {
errs = append(errs, fmt.Errorf("item %s: %w", item.ID, err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}panic ne zaman kullanılmalı?
Beklenen hatalar için asla. Yalnızca üç durumda:
- Kurtarılamaz hata: "olması mümkün olmayan" bir noktada ihlal edilen bir değişmez (
panic("unreachable")). - Bağımsız bir programın
init()/main()dosyasında başarısız başlatma (kritik yapılandırma eksik → hızlı başarısız). - Bunu açıkça belgeleyen Kitaplık API'si: örneğin,
regexp.MustCompile(desen geçersizse paniğe kapılır; desen bir derleme zamanı sabiti olduğundan kabul edilebilir).
Geriye kalan her şey error. Yaymak için panic kısayolunu kullanmayın: kompozisyonu bozar, defer Close() atlar ve arayanı bunaltır.
Egzersizler
Os.ReadFile hatasını fmt.Errorf ile %w fiili kullanarak sarın ve 'config:' (yol dosyası) yarışmasına ekleyin.
3 denemeden sonra çözüm mevcut
ABD hataları. Sarma dosyasında hata olup olmadığını doğrulamak için tüm ErrNotFound gönderilerini ve 'manca!' damgasını vurun. Tal Caso'da.
3 denemeden sonra çözüm mevcut
Bir Go kütüphanesinde panik nasıl kullanılır?
func F(x int) {
// ??? panic(...)
}