60 / 68

errors (chuqurroq)

errors paketi: chuqurroq nazar

Golang'ning "Errors" darsida errors.New() va shaxsiy xatolik turlarini yaratishning asosini ko'rgan edik. Bu darsda errors paketining ancha kuchli imkoniyatlariga to'xtalamiz: xatoliklarni "o'rash" (wrapping), ma'lum bir xatolikni aniqlash (Is) va undan ma'lumot olish (As), hamda bir nechta xatolikni birlashtirish (Join).

Bu imkoniyatlar ayniqsa katta dasturlarda, xatolik bir funksiyadan ikkinchisiga "ko'tarilib" borganda, dastlabki sababni yo'qotmasdan uzatish uchun juda muhim.

example.go
import "errors"

1. Xatolikni "o'rash" (wrapping): %w

Funksiya ichida xatolik yuz berganda, uni "yalang'och" holda emas, balki qo'shimcha kontekst bilan birga qaytarish foydali. fmt.Errorf()dagi maxsus %w format spetsifikatori aynan shu uchun mo'ljallangan — u asl xatolikni yangisi ichiga "o'raydi":

example.go
package main

import (
    "errors"
    "fmt"
)

// paket darajasidagi, oldindan e'lon qilingan xatolik
var ErrNotFound = errors.New("topilmadi")

func findUser(id int) error {
    if id == 0 {
        // %w orqali ErrNotFound'ni yangi xabar ichiga o'raymiz
        return fmt.Errorf("foydalanuvchi qidirishda xatolik: %w", ErrNotFound)
    }
    return nil
}

func main() {
    err := findUser(0)
    fmt.Println(err)
}

Natija:

kod
foydalanuvchi qidirishda xatolik: topilmadi

[!IMPORTANT] %w bilan %v(yoki %s) orasidagi farq muhim: %v xatolikni shunchaki matn sifatida "yopishtirib qo'yadi" va asl xatolik haqidagi ma'lumot yo'qoladi. %w esa asl xatolikni ichida saqlab qoladi, shuning uchun keyinroq uni errors.Is()/errors.As() orqali qayta tiklash mumkin bo'ladi.


2. Xatolik aynan qaysi turdan ekanligini tekshirish: errors.Is()

Agar funksiya "o'ralgan" xatolik qaytargan bo'lsa, uning ichida ma'lum bir xatolik (masalan, ErrNotFound) bor-yo'qligini == orqali emas, errors.Is() orqali tekshiramiz:

example.go
package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("topilmadi")

func findUser(id int) error {
    if id == 0 {
        return fmt.Errorf("foydalanuvchi qidirishda xatolik: %w", ErrNotFound)
    }
    return nil
}

func main() {
    err := findUser(0)

    // err "yopilgan" xabar bo'lsa ham, ichida ErrNotFound borligini topadi
    if errors.Is(err, ErrNotFound) {
        fmt.Println("Foydalanuvchi topilmadi!")
    }
}

Natija:

kod
Foydalanuvchi topilmadi!

[!WARNING] err == ErrNotFound yozish ishlamaydi, chunki err — bu fmt.Errorf() yaratgan YANGI xatolik obyekti, ErrNotFoundning o'zi emas (u faqat ichiga "o'ralgan"). Shu sababli "o'ralgan" xatoliklarni solishtirish uchun har doim errors.Is() ishlatilishi shart.


3. O'ralgan xatolikdan ma'lumot olish: errors.As()

Agar xatolik oddiy matn emas, balki qo'shimcha ma'lumotga ega maxsus tur (struct) bo'lsa va o'sha ma'lumotga (masalan, qaysi maydon xato ekanligiga) kirish kerak bo'lsa — errors.As() ishlatiladi:

example.go
package main

import (
    "errors"
    "fmt"
)

// shaxsiy xatolik turi, qo'shimcha ma'lumot (Field) bilan
type ValidationError struct {
    Field string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("%s maydoni noto'g'ri", e.Field)
}

func validate() error {
    return &ValidationError{Field: "email"}
}

func main() {
    err := validate()

    var valErr *ValidationError

    // agar err (yoki ichiga o'ralgan xatolik) *ValidationError bo'lsa,
    // uni valErr o'zgaruvchisiga "chiqarib" beradi
    if errors.As(err, &valErr) {
        fmt.Println("Muammoli maydon:", valErr.Field)
    }
}

Natija:

kod
Muammoli maydon: email

errors.As() — bu, aslida, oddiy type assertion (err.(*ValidationError))ning "chuqurroq" versiyasi: u nafaqat errning o'zini, balki uning ichiga o'ralgan barcha xatoliklar zanjirini ham tekshirib chiqadi.


4. Xatolikni "ochish": errors.Unwrap()

Agar o'ralgan xatolik ichidagi asl xatolikni to'g'ridan-to'g'ri olish kerak bo'lsa (kamdan-kam holatlarda kerak bo'ladi, odatda Is/As yetarli):

example.go
package main

import (
    "errors"
    "fmt"
)

func main() {
    inner := errors.New("ichki xatolik")
    outer := fmt.Errorf("tashqi qatlam: %w", inner)

    unwrapped := errors.Unwrap(outer)

    fmt.Println(unwrapped)
}

Natija:

kod
ichki xatolik

5. Bir nechta xatolikni birlashtirish: errors.Join()

Ba'zan bir vaqtning o'zida bir nechta mustaqil xatolik yuz berishi mumkin (masalan, formadagi bir nechta maydon noto'g'ri to'ldirilgan bo'lsa). Ularni bittaga birlashtirish uchun errors.Join() ishlatiladi:

example.go
package main

import (
    "errors"
    "fmt"
)

func main() {
    err1 := errors.New("birinchi xato")
    err2 := errors.New("ikkinchi xato")

    joined := errors.Join(err1, err2)

    fmt.Println(joined)

    // Is() birlashtirilgan xatolik ichidan har ikkalasini ham topa oladi
    fmt.Println(errors.Is(joined, err1))
    fmt.Println(errors.Is(joined, err2))
}

Natija:

kod
birinchi xato
ikkinchi xato
true
true

Xulosa: qachon qaysi funksiya kerak?

VazifaFunksiya
Xatolikka qo'shimcha kontekst qo'shib qaytarishfmt.Errorf("...: %w", err)
Ma'lum bir xatolik yuz berganini tekshirisherrors.Is(err, MaqsadXato)
O'ralgan xatolikdan maxsus ma'lumot olisherrors.As(err, &maqsadTur)
Bir qatlamni "ochish"errors.Unwrap(err)
Bir nechta xatolikni birlashtirisherrors.Join(err1, err2, ...)

[!NOTE] Amaliy qoida: agar funksiyangiz boshqa funksiyadan kelgan xatolikni qaytarayotgan bo'lsa, uni doim %w bilan o'rab qaytaring (shunchaki qayta yozib chiqarish yoki %v ishlatish o'rniga). Bu chaqiruvchi tomonga xatolikning "haqiqiy sababi"gacha yetib borish imkonini beradi.