Üç tür hata
Go kodu en basitten en zengine üç biçimde hata kullanır:
- Geçici bir hata: yerinde oluşturulan
errors.New("...")ya dafmt.Errorf("..."). Çağıranlar yalnızca mesajı okuyabilir. - Bir sentinel hata:
io.EOFgibi paket seviyesinde bir değişken. Çağıranlar onuerrors.Isile test edebilir. - Bir hata tipi: alanlar taşıyan,
Error()metodu olan bir struct. Çağıranlar onuerrors.Asile çıkarıp alanlarını okuyabilir.
Çağıranların ihtiyaç duyduğu şeyi yapmasına izin veren en basit olanı seçin.
Sentinel hatalar
Sentinel, bir kez tanımlanan ve kimliğine göre karşılaştırılan bir hata değeridir. Adlar geleneksel olarak Err ile başlar.
Çıktı:
mug bought
cap is out of stock, notify me later
buy "hat": not found
Aynı metinli iki errors.New çağrısı farklı hatalar üretir: errors.New("x") == errors.New("x") sonucu false'tur. Bir sentinel'in tek bir paylaşılan değişken olması gerekmesinin nedeni budur.
Sentinel'ler paketinizin API'sinin bir parçası haline gelir. Çağıranlar ErrNotFound'u kontrol etmeye başladığında onları bozmadan onu döndürmeyi bırakamazsınız. Yalnızca çağıranların gerçekten dallanması gerekenleri dışa açın.
Özel hata tipleri
Çağıranın ayrıntılara (hangi alan, hangi durum kodu, hangi yeniden deneme gecikmesi) ihtiyacı olduğunda bir tip tanımlayın.
Çıktı:
register: age: must be between 0 and 150
bad field: age
Metodun pointer alıcısı var ve fonksiyon &ValidationError{...} döndürüyor, bu yüzden errors.As'in hedefi bir *ValidationError'dır ve onun adresini (&ve, bir **ValidationError) aktarırsınız. Bu seviyeyi karıştırmak klasik errors.As hatasıdır: değer alıcıyla ValidationError{...} döndürür ve var ve ValidationError tanımlardınız. go vet en yaygın kaymayı, &ve yerine ve aktarmayı yakalar: second argument to errors.As must be a non-nil pointer to either a type that implements error, or to any interface type.
%w ile sarmalamak
%w ile fmt.Errorf, sarmaladığı hatayı hatırlayan bir hata döndürür. Her katman bağlam ekler ve zinciri bozmadan tutar.
Çıktı:
start: connect db: timeout
*fmt.wrapError: start: connect db: timeout
*fmt.wrapError: connect db: timeout
*errors.errorString: timeout
true
false
%v ile mesaj aynıdır ama zincir kesilir, bu yüzden errors.Is false döndürür. Çağıranların nedeni görebilmesi gerekiyorsa %w, neden onların bağımlı olmasını istemediğiniz bir uygulama ayrıntısıysa %v seçin.
Go 1.20'den beri tek bir çağrı birkaç hatayı sarmalayabilir: fmt.Errorf("%w; %w", err1, err2). errors.Is bu durumda ikisinden biriyle eşleşir.
errors.Join: aynı anda birkaç hata
Doğrulama ve temizlik çoğu zaman birden fazla hata üretir. errors.Join (Go 1.20) onları bir araya getirir.
Çıktı:
name: required
email: required
age: negative
---
true
true
Her argüman nil olduğunda errors.Join nil döndürür, bu yüzden yukarıdaki fonksiyonun "hata yok" için özel bir duruma ihtiyacı yoktur.
Unwrap ve Is metotları
errors.Is ve errors.As, sarmalanmış hataları bir Unwrap metodunu çağırarak bulur. Bir neden tutan özel bir tip onu dışa sunmalıdır:
Birkaç hatayı sarmalayan bir tip bunun yerine Unwrap() []error gerçekleştirir.
Bir tip eşitliğe kendisi karar vermek için Is(target error) bool de tanımlayabilir; örneğin aynı durum koduna sahip herhangi bir *HTTPError ile eşleşmek için. Buna nadiren ihtiyaç duyarsınız; bir sentinel'i sarmalayıp Unwrap'tan döndürmek genellikle işi görür.
Tipli nil tuzağı
Bir hata değişkenini asla somut tipinizle tanımlayıp error üzerinden döndürmeyin:
func check() error {
var err *ValidationError // nil pointer
// ... no problem found
return err // non-nil error! Its type is *ValidationError
}
Çağıranın if err != nil'i true olur, çünkü tipli nil bir pointer tutan bir interface nil değildir. Başarıda düz bir nil döndürün ve yerel hata değişkenlerini error tipinde tutun. Nedenini interface'ler sayfası açıklıyor.
Hangi türü seçmeli
| Çağıranların yapması gereken | Sağlayın |
|---|---|
| yalnızca başarısızlığı loglamak ya da göstermek | fmt.Errorf("...: %w", err) |
| belirli bir duruma göre dallanmak | bir sentinel var ErrX = errors.New(...) |
| başarısızlıkla ilgili ayrıntıları okumak | alanları olan bir hata tipi |
| birkaç bağımsız başarısızlığı görmek | errors.Join |
Sık yapılan hatalar
- Sarmalanmış hataları
==ile karşılaştırmak.errors.Iskullanın. errors.As'e pointer olmayan bir şey aktarmak. Hedef tipte bir değişkene pointer ister.- Bir fonksiyonun içinde "sentinel" oluşturmak.
return errors.New("not found")her çağrıda yeni bir değer oluşturur; çağıranlar onunla karşılaştırma yapamaz. - Her hatayı dışa açmak. Dışa açılan her sentinel ya da tip bir API sözüdür.
err.Error()'a göre eşleştirmek. String'ler insanlar içindir.
Sıkça Sorulan Sorular
Go'da özel bir hata nasıl oluşturulur?
Sabit bir durum için paket seviyesinde bir sentinel tanımlayın: var ErrNotFound = errors.New("not found"). Veri taşıyan bir hata için Error() string metodu olan bir tip tanımlayın: type ValidationError struct { Field string } ve func (e *ValidationError) Error() string { return e.Field + " is invalid" }.
Go'da bir hata nasıl sarmalanır?
%w verb'ü ile fmt.Errorf kullanın: return fmt.Errorf("load user %d: %w", id, err). Yeni hatanın mesajı eskisini içerir ve errors.Unwrap, errors.Is ve errors.As orijinale ulaşabilir. Go 1.20'den beri tek bir Errorf çağrısı birkaç %w verb'ü ile birkaç hatayı sarmalayabilir.
errors.Is ile errors.As arasındaki fark nedir?
errors.Is(err, target), io.EOF gibi sentinel'ler için "bu belirli hata değeri zincirin herhangi bir yerinde var mı" sorusunu yanıtlar. errors.As(err, &target) ise "zincirde bu tipte bir hata var mı" sorusunu yanıtlar ve varsa alanlarını okuyabilmeniz için onu target içine saklar.
errors.Join ne yapar?
errors.Join(errs...) (Go 1.20) birkaç hatayı tek bir hatada birleştirir. Mesajı, satır sonlarıyla ayrılmış tek tek mesajlardır; nil hatalar atılır ve hepsi nil ise nil döndürür. Birleştirilen hatalardan herhangi biri eşleşirse errors.Is ve errors.As eşleşir.