14-bo‘lim

Xatolar bilan ishlash

error interfeysi, errors.New va fmt.Errorf, xatoni o'rash, errors.Is va errors.As, nishon xatolar hamda o'z xato turlaringiz.

🕑 12 daqiqa o‘qish 📄 680 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. error - bu shunchaki interfeys
  2. fmt.Errorf bilan tafsilot qo'shish
  3. Xatoni o'rash
  4. %w va %v farqi
  5. O'z xato turingiz
  6. Bir nechta xatoni birlashtirish
  7. Xatolarni qayerda hal qilish kerak
  8. Xatoni e'tiborsiz qoldirish
  9. Xulosa

Go da xato - oddiy qiymat. Uni qaytarasiz, tekshirasiz, uzatasiz. Hech qanday try, catch yoki throw yo'q.

error - bu shunchaki interfeys #

Go
package main

import (
    "errors"
    "fmt"
)

func bol(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("nolga bo'lish mumkin emas")
    }
    return a / b, nil
}

func main() {
    natija, xato := bol(10, 4)
    if xato != nil {
        fmt.Println("xato:", xato)
        return
    }
    fmt.Println(natija)

    if _, xato := bol(1, 0); xato != nil {
        fmt.Println("xato:", xato)
    }
}
Natija
2.5
xato: nolga bo'lish mumkin emas

error - standart kutubxonadagi interfeys va u bitta metoddan iborat:

Go
type error interface {
    Error() string
}

Ya'ni Error() string metodi bor har qanday tur xato bo'la oladi.

fmt.Errorf bilan tafsilot qo'shish #

Go
package main

import (
    "fmt"
    "strconv"
)

func yoshniOqi(matn string) (int, error) {
    son, xato := strconv.Atoi(matn)
    if xato != nil {
        return 0, fmt.Errorf("yosh noto'g'ri: %w", xato)
    }
    if son < 0 || son > 150 {
        return 0, fmt.Errorf("yosh chegaradan tashqarida: %d", son)
    }
    return son, nil
}

func main() {
    fmt.Println(yoshniOqi("25"))
    fmt.Println(yoshniOqi("salom"))
    fmt.Println(yoshniOqi("200"))
}
Natija
25 <nil>
0 yosh noto'g'ri: strconv.Atoi: parsing "salom": invalid syntax
0 yosh chegaradan tashqarida: 200

fmt.Errorf - Sprintf kabi, lekin satr emas, xato qaytaradi.

%w shabloni maxsus: u ichkaridagi xatoni o'rab saqlaydi. Buni quyida ko'ramiz.

Xatoni o'rash #

Go
package main

import (
    "errors"
    "fmt"
)

var XatoTopilmadi = errors.New("topilmadi")

func bazadanOl(id int) error {
    if id > 100 {
        return fmt.Errorf("talaba %d: %w", id, XatoTopilmadi)
    }
    return nil
}

func main() {
    xato := bazadanOl(500)

    fmt.Println(xato)
    fmt.Println(errors.Is(xato, XatoTopilmadi))
    fmt.Println(errors.Unwrap(xato) == XatoTopilmadi)

    fmt.Println(errors.Is(bazadanOl(5), XatoTopilmadi))
}
Natija
talaba 500: topilmadi
true
true
false

Bu Go dagi juda muhim naqsh:

  1. XatoTopilmadi - nishon xato (sentinel). U paket darajasida bir marta e'lon qilinadi.
  2. %w bilan o'ralganda, tashqi xato kontekst qo'shadi, lekin ichkarisi saqlanadi.
  3. errors.Is butun zanjir bo'ylab qidiradi.
Xatoni matn bo'yicha taqqoslamang

Bu juda keng tarqalgan xato:

Go
if xato.Error() == "topilmadi" {

Nega yomon: xato matni odamlar uchun yozilgan. Kimdir uni aniqroq qilib o'zgartirsa yoki kontekst qo'shsa, tekshiruv jimgina ishlamay qoladi.

To'g'ri yo'l - errors.Is:

Go
if errors.Is(xato, XatoTopilmadi) {

U o'ralgan xatolar zanjirini ham tekshiradi.

%w va %v farqi #

Go
package main

import (
    "errors"
    "fmt"
)

var Asosiy = errors.New("asosiy xato")

func main() {
    oralgan := fmt.Errorf("kontekst: %w", Asosiy)
    oddiy := fmt.Errorf("kontekst: %v", Asosiy)

    fmt.Println(oralgan)
    fmt.Println(oddiy)

    fmt.Println(errors.Is(oralgan, Asosiy))
    fmt.Println(errors.Is(oddiy, Asosiy))
}
Natija
kontekst: asosiy xato
kontekst: asosiy xato
true
false

Ikkala xato bir xil ko'rinadi, lekin ular boshqacha.

%v xatoni oddiy matnga aylantiradi va bog'lanish uziladi. %w esa asl xatoni saqlab qoladi.

Qoida: xatoni uzatayotganingizda deyarli har doim %w.

O'z xato turingiz #

Go
package main

import (
    "errors"
    "fmt"
)

type TekshiruvXatosi struct {
    Maydon string
    Sabab  string
}

func (x *TekshiruvXatosi) Error() string {
    return fmt.Sprintf("%s: %s", x.Maydon, x.Sabab)
}

func tekshir(ism string, yosh int) error {
    if ism == "" {
        return &TekshiruvXatosi{Maydon: "ism", Sabab: "bo'sh"}
    }
    if yosh < 0 {
        return &TekshiruvXatosi{Maydon: "yosh", Sabab: "manfiy"}
    }
    return nil
}

func main() {
    xato := tekshir("", 20)
    fmt.Println(xato)

    var tx *TekshiruvXatosi
    if errors.As(xato, &tx) {
        fmt.Println("muammoli maydon:", tx.Maydon)
    }

    fmt.Println(tekshir("Malika", 20))
}
Natija
ism: bo'sh
muammoli maydon: ism
<nil>

errors.As - "zanjirda shu turdagi xato bormi" degan savolga javob beradi va topilsa, uni ko'rsatilgan o'zgaruvchiga yozadi.

FunksiyaSavol
errors.IsBu aynan o'sha xatomi
errors.AsZanjirda shu turdagi xato bormi
errors.UnwrapBir qavat ichkariga
errors.JoinBir nechta xatoni birlashtirish
errors.As ga ko'rsatkichning ko'rsatkichi beriladi

errors.As(xato, &tx) dagi & ni tushuntirish kerak.

tx ning o'zi allaqachon *TekshiruvXatosi - ya'ni ko'rsatkich. errors.As esa topilgan xatoni unga yozishi kerak, shuning uchun unga o'zgaruvchining manzili - &tx beriladi.

Bu 8-bo'limdagi qoidaning aynan o'zi: funksiya o'zgaruvchini o'zgartirishi uchun uning manzili kerak.

Bir nechta xatoni birlashtirish #

Go
package main

import (
    "errors"
    "fmt"
)

func hammasiniTekshir(ism, email string, yosh int) error {
    var xatolar []error

    if ism == "" {
        xatolar = append(xatolar, errors.New("ism bo'sh"))
    }
    if email == "" {
        xatolar = append(xatolar, errors.New("email bo'sh"))
    }
    if yosh < 18 {
        xatolar = append(xatolar, errors.New("yosh 18 dan kichik"))
    }

    return errors.Join(xatolar...)
}

func main() {
    xato := hammasiniTekshir("", "", 15)
    fmt.Println(xato)

    fmt.Println("---")
    fmt.Println(hammasiniTekshir("Aziza", "[email protected]", 20))
}
Natija
ism bo'sh
email bo'sh
yosh 18 dan kichik
---
<nil>

errors.Join (Go 1.20) bir nechta xatoni bitta qilib birlashtiradi. Chop etilganda har biri yangi qatorda chiqadi.

Agar hamma argument nil bo'lsa, natija ham nil bo'ladi - shuning uchun ikkinchi chaqiruv <nil> berdi.

Xatolarni qayerda hal qilish kerak #

Go
package main

import (
    "errors"
    "fmt"
    "strconv"
)

var XatoBuzuqQator = errors.New("qator buzuq")

func qatorniOqi(qator string) (int, error) {
    son, xato := strconv.Atoi(qator)
    if xato != nil {
        return 0, fmt.Errorf("%q: %w", qator, XatoBuzuqQator)
    }
    return son, nil
}

func hammasiniQosh(qatorlar []string) (int, error) {
    jami := 0
    for i, q := range qatorlar {
        son, xato := qatorniOqi(q)
        if xato != nil {
            return 0, fmt.Errorf("%d-qator: %w", i+1, xato)
        }
        jami += son
    }
    return jami, nil
}

func main() {
    fmt.Println(hammasiniQosh([]string{"10", "20", "30"}))

    _, xato := hammasiniQosh([]string{"10", "buzuq", "30"})
    fmt.Println(xato)
    fmt.Println(errors.Is(xato, XatoBuzuqQator))
}
Natija
60 <nil>
2-qator: "buzuq": qator buzuq
true

Har bir daraja o'z kontekstini qo'shadi va xatoni yuqoriga uzatadi. Natijada xabar butun yo'lni ko'rsatadi.

Xato faqat eng yuqorida - foydalanuvchiga ko'rsatiladigan joyda hal qilinadi.

Kontekst qo'shing, lekin takrorlamang

Yaxshi xato xabari o'qiganda hamma narsani aytadi: 2-qator: "buzuq": qator buzuq.

Ikki keng tarqalgan xato bor:

  1. Kontekstsiz uzatish: return xato. Shunda oxirida faqat invalid syntax qoladi va qayerdan kelgani noma'lum.
  2. Takrorlash: return fmt.Errorf("xato yuz berdi: %w", xato). "Xato yuz berdi" hech qanday yangi ma'lumot bermaydi.

Qoida: har bir daraja o'zi biladigan narsani qo'shsin - qator raqami, fayl nomi, identifikator.

Va yana: xato matnini kichik harf bilan boshlang va oxiriga nuqta qo'ymang. Chunki u boshqa xabar ichiga qo'yilishi mumkin.

Xatoni e'tiborsiz qoldirish #

Go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    son, _ := strconv.Atoi("42")
    fmt.Println(son)

    buzuq, _ := strconv.Atoi("salom")
    fmt.Println(buzuq)
}
Natija
42
0

_ bilan xatoni ataylab tashlab yuborish mumkin.

Buni juda ehtiyotkorlik bilan qiling

Ikkinchi misolda buzuq 0 bo'lib qoldi va dastur hech narsa sezmadi. Agar bu haqiqiy hisobda ishlatilsa, natija jimgina noto'g'ri bo'lardi.

_ ni faqat xato haqiqatan ham mumkin bo'lmagan joyda ishlating - masalan, o'zingiz yozgan doimiy satrni o'qiyotganda.

Shubha bo'lsa, tekshiring. Go da ortiqcha uch qator yozish - eng arzon himoya.

Amaliy topshiriq
  1. errors.New bilan xato qaytaruvchi funksiya yozing.
  2. fmt.Errorf bilan kontekst qo'shing.
  3. Nishon xato e'lon qilib, errors.Is bilan tekshiring.
  4. %w va %v farqini isbotlang.
  5. O'z xato turingizni Error() metodi bilan yozing.
  6. errors.As bilan xato turidan maydon oling.
  7. errors.Join bilan uch xatoni birlashtiring.
  8. Ikki darajali funksiyada kontekst qo'shib uzating.
  9. Xatoni matn bo'yicha taqqoslab, nega yomonligini o'ylang.
  10. _ bilan xatoni tashlab, oqibatini ko'ring.

Xulosa #

  • error - bitta metodli interfeys; xato - oddiy qiymat.
  • errors.New sodda xato, fmt.Errorf esa kontekstli xato yaratadi.
  • %w ichkaridagi xatoni saqlaydi, %v esa bog'lanishni uzadi.
  • errors.Is aynan o'sha xatoni, errors.As esa o'sha turdagi xatoni qidiradi - ikkalasi ham butun zanjir bo'ylab.
  • Xatoni matn bo'yicha taqqoslamang.
  • errors.Join bir nechta xatoni birlashtiradi; hammasi nil bo'lsa, natija ham nil.
  • Har bir daraja o'z kontekstini qo'shib uzatsin; xato eng yuqorida hal qilinadi.
  • Xato matni kichik harf bilan boshlanadi va nuqtasiz tugaydi.

Keyingi bo'limda panic va recover ni o'rganamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.