- Xəta qaytaran funksiya yazmaq və
if err != nililə yoxlamaq - Xətaları
%wilə bükmək vəerrors.Is,errors.Asilə yoxlamaq panicvərecover-in nə vaxt işlədildiyini izah etmək
Fayl tapılmaya bilər, internet kəsilə bilər, istifadəçi ədəd əvəzinə hərf yaza bilər. Bir çox dil belə hallarda istisna (exception) atır və onu try/catch ilə tutur. Go başqa yol seçib: xəta — funksiyanın qaytardığı adi qiymətdir. Bu, kodu bir az uzadır, amma hər xətanın harada yarana biləcəyini və necə emal olunduğunu açıq görürsən.
Xəta — adi qiymətdir
Go-da error sadə bir interfeysdir: Error() string metodu olan istənilən tip xətadır. Qayda belədir: uğursuz ola bilən funksiya xətanı son nəticə kimi qaytarır, uğurlu halda isə nil qaytarır. Çağıran tərəf dərhal yoxlayır: if err != nil { ... }. Ən sadə xətanı errors.New("mətn") yaradır.
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 4)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("result:", result)
if _, err := divide(1, 0); err != nil {
fmt.Println("error:", err)
}
}result: 2.5 error: division by zero
Diqqət et: xəta olanda nəticə kimi 0 qaytarılır, amma çağıran tərəf ona baxmır — əvvəlcə err yoxlanılır. Əsas kod sola yaxın qalır, xəta isə tez emal olunur və funksiyadan çıxılır.
Xətaları bükmək: %w və errors.Is
Xəta yuxarı ötürüləndə ona kontekst əlavə etmək faydalıdır: nə edərkən xəta baş verdi? fmt.Errorf("load profile %d: %w", id, err) yeni xəta yaradır və orijinalı içində bükür (wrap). %w işarəsi vacibdir: orijinal xətanı məhz o saxlayır. Sonra errors.Is(err, ErrNotFound) zənciri axıra qədər yoxlayır və içəridə axtarılan xətanın olub-olmadığını deyir. Əvvəlcədən elan olunmuş belə xətalara gözətçi xətalar (sentinel errors) deyilir, adları isə adətən Err ilə başlayır.
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func findStudent(id int) (string, error) {
students := map[int]string{1: "Aysel", 2: "Murad"}
name, ok := students[id]
if !ok {
return "", ErrNotFound
}
return name, nil
}
func loadProfile(id int) (string, error) {
name, err := findStudent(id)
if err != nil {
return "", fmt.Errorf("load profile %d: %w", id, err)
}
return "Profile of " + name, nil
}
func main() {
p, err := loadProfile(2)
fmt.Println(p, err)
_, err = loadProfile(7)
fmt.Println(err)
fmt.Println(errors.Is(err, ErrNotFound), err == ErrNotFound)
}Profile of Murad <nil> load profile 7: not found true false
Son sətrə bax: bükülmüş xəta artıq ErrNotFound-a bərabər deyil (== false verir), amma errors.Is onu zəncirin içində tapır. Ona görə xətaları həmişə errors.Is ilə yoxla.
if err != nil && err.Error() == "not found" {
fmt.Println("no such student")
}if errors.Is(err, ErrNotFound) {
fmt.Println("no such student")
}load profile 7: not found) və birinci variant artıq işləmir. errors.Is isə bükülmüş xətaları da tanıyır.Öz xəta tipin və errors.As
Bəzən xəta haqqında mətndən çox məlumat lazımdır: hansı sahə səhvdir, hansı qiymət gəlib? Onda öz xəta tipini yarat — Error() string metodu olan struktur. Bükülmüş zəncirdən belə xətanı çıxarmaq üçün errors.As işlədilir: o, zəncirdə uyğun tipli xəta tapırsa, onu sənin dəyişəninə yazır və true qaytarır.
package main
import (
"errors"
"fmt"
)
type ValidationError struct {
Field string
Value int
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("invalid %s: %d", e.Field, e.Value)
}
func setAge(age int) error {
if age < 0 || age > 120 {
return &ValidationError{Field: "age", Value: age}
}
return nil
}
func main() {
err := fmt.Errorf("register user: %w", setAge(-3))
fmt.Println(err)
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("field:", ve.Field, "value:", ve.Value)
}
fmt.Println(setAge(30) == nil)
}register user: invalid age: -3 field: age value: -3 true
| Alət | Nə edir |
|---|---|
errors.New("...") | sadə xəta yaradır |
fmt.Errorf("ctx: %w", err) | kontekst əlavə edib xətanı bükür |
errors.Is(err, ErrX) | zəncirdə konkret xəta varmı? |
errors.As(err, &target) | zəncirdən müəyyən tipli xətanı çıxarır |
panic və recover
panic — proqramın normal işini dayandıran fövqəladə vəziyyətdir: massivin hüdudundan kənara çıxmaq, nil göstərici, tam ədədin sıfıra bölünməsi. Panic zamanı funksiyalar bir-bir tərk edilir, təxirə salınmış (defer) çağırışlar isə icra olunur. Əgər təxirə salınmış funksiyada recover() çağırılsa, panic dayanır və proqram işini davam etdirir.
package main
import "fmt"
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered: %v", r)
}
}()
result = a / b
return result, nil
}
func main() {
fmt.Println(safeDivide(10, 2))
fmt.Println(safeDivide(1, 0))
fmt.Println("program continues")
}5 <nil> 0 recovered: runtime error: integer divide by zero program continues
safeDivide(1, 0) panic yaratdı, amma təxirə salınmış funksiya onu recover ilə tutdu və adlı nəticəyə — err-ə adi xəta yazdı. Adlı nəticə burada vacibdir: məhz onun sayəsində defer funksiyası qaytarılan qiyməti dəyişə bilir.
Əsas fikirlər
- Xəta —
Error() stringmetodu olan qiymətdir; funksiya onu son nəticə kimi qaytarır, uğurda isənil. if err != nilyoxlamasını dərhal et və xətanı kontekstlə yuxarı ötür:fmt.Errorf("...: %w", err).- Bükülmüş xətaları
==və mətn müqayisəsi ilə yox,errors.Is(konkret xəta) vəerrors.As(xəta tipi) ilə yoxla. - Öz xəta tipin əlavə məlumat (sahə, qiymət) daşıya bilər.
panicyalnız fövqəladə hallar üçündür;recoveronu yalnız təxirə salınmış funksiyada tuta bilər.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.