Go’da xatolarni boshqarish
Dastur ishlayotganda fayl topilmasligi, noto‘g‘ri qiymat kiritilishi yoki tarmoq so‘rovi bajarilmasligi mumkin. Go’da bunday kutiladigan muammolar error qiymati orqali boshqariladi.
Har bir funksiya error qaytarmaydi. Faqat bajarilishi muvaffaqiyatsiz tugashi mumkin bo‘lgan funksiya odatda natija bilan birga error qaytaradi.
error nima?
error standart interfeys bo‘lib, bitta metodga ega:
Xato bo‘lmasa, funksiya nil qaytaradi. Xato yuz bersa, uning sababini ifodalovchi error qiymati qaytariladi.
Quyidagi funksiya ikki float64 sonni bo‘ladi:
package main
import (
"errors"
"fmt"
)
func bolish(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("nolga bo‘lish mumkin emas")
}
return a / b, nil
}
func main() {
natija, err := bolish(10, 4)
if err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println("Natija:", natija)
}
Natija:
bolish() muvaffaqiyatli bajarilganda hisob natijasi va nil qaytaradi. Ikkinchi argument nol bo‘lsa, natijaning nol qiymati va xato qaytariladi.
Xatoni tekshirish
Xato qaytaradigan funksiya chaqirilganda avval err tekshiriladi:
Natija:
Xatodan keyin natijani ishlatish xavfli bo‘lishi mumkin. Shu sababli xato aniqlanganda uni qaytarish, foydalanuvchiga tushunarli xabar ko‘rsatish yoki boshqa mos qaror qabul qilish kerak.
Bu “har bir funksiyada xato tekshiriladi” degani emas. Faqat funksiya error qaytarsa, chaqiruvchi uni e’tiborsiz qoldirmasligi kerak. Ayrim hollarda xatoni ataylab e’tiborsiz qoldirish mumkin, lekin sabab koddan aniq ko‘rinishi lozim.
Xato yaratish
O‘zgarmas matnli sodda xato uchun errors.New() ishlatiladi:
Xato xabariga qiymat qo‘shish kerak bo‘lsa, fmt.Errorf() qulay:
func yoshniTekshir(yosh int) error {
if yosh < 0 {
return fmt.Errorf("noto‘g‘ri yosh: %d", yosh)
}
return nil
}
Xato matni qisqa va muammoning sababini tushuntiradigan bo‘lsin. Odatda xato matni kichik harf bilan boshlanadi va oxiriga nuqta qo‘yilmaydi. Bu xatoga yuqori qatlamlarda qo‘shimcha matn biriktirishni osonlashtiradi.
Xatoni yuqoriga uzatish
Funksiya xatoni o‘zi hal qila olmasa, uni chaqiruvchiga qaytaradi. Faqat return err qilish mumkin, ammo xato qaysi amal paytida yuz bergani yo‘qolib qoladi.
Quyidagi misolda xato uch qatlam orqali yuqoriga uzatiladi:
package main
import (
"fmt"
"os"
)
func faylniOqish(nom string) ([]byte, error) {
data, err := os.ReadFile(nom)
if err != nil {
return nil, fmt.Errorf("%q faylini o‘qish: %w", nom, err)
}
return data, nil
}
func sozlamaniYuklash() ([]byte, error) {
data, err := faylniOqish("config.json")
if err != nil {
return nil, fmt.Errorf("sozlamani yuklash: %w", err)
}
return data, nil
}
func run() error {
data, err := sozlamaniYuklash()
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}
func main() {
if err := run(); err != nil {
fmt.Println("Dastur ishga tushmadi:", err)
}
}
config.json mavjud bo‘lmasa, xabar taxminan quyidagicha bo‘ladi:
Dastur ishga tushmadi: sozlamani yuklash: "config.json" faylini o‘qish: open config.json: no such file or directory
Har bir qatlam o‘zi bajarayotgan amal haqida kontekst qo‘shdi. Xato faqat foydalanuvchiga xabar berish mumkin bo‘lgan yuqori qatlamda chiqarildi. Pastki funksiyalar xatoni chiqarib, yana qaytarmadi. Bu bitta xatoning logda bir necha marta takrorlanishiga yo‘l qo‘ymaydi.
%w asl xatoni o‘raydi (wrap). Natijada xato matniga kontekst qo‘shiladi va asl sababni keyin ham tekshirish mumkin bo‘ladi. Oddiy %v matnni qo‘shadi, lekin xatolar zanjirini saqlamaydi.
errors.Is() bilan sababni tekshirish
O‘ralgan xatoni matni orqali solishtirish ishonchsiz. Ma’lum xato qiymatini zanjir ichidan topish uchun errors.Is() ishlatiladi:
package main
import (
"errors"
"fmt"
"os"
)
func faylniOqish(nom string) ([]byte, error) {
data, err := os.ReadFile(nom)
if err != nil {
return nil, fmt.Errorf("%q faylini o‘qish: %w", nom, err)
}
return data, nil
}
func main() {
_, err := faylniOqish("config.json")
if errors.Is(err, os.ErrNotExist) {
fmt.Println("Sozlama fayli topilmadi")
return
}
if err != nil {
fmt.Println("Xato:", err)
}
}
faylniOqish() xatoga kontekst qo‘shgan bo‘lsa ham, errors.Is() zanjir ichidagi os.ErrNotExist sababini topadi.
Dasturga tegishli doimiy xato holati uchun sentinel error e’lon qilish mumkin:
Chaqiruvchi uni errors.Is(err, ErrUserNotFound) bilan tekshiradi. Sentinel error faqat chaqiruvchi shu holatga alohida munosabat bildirishi kerak bo‘lganda foydali.
Custom error va errors.As()
Xatoga faqat matn emas, qo‘shimcha ma’lumot ham biriktirish kerak bo‘lsa, maxsus xato turi (custom error type) yaratiladi.
Masalan, forma maydonidagi xatoda maydon nomini alohida saqlaymiz:
package main
import (
"errors"
"fmt"
)
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func userYarat(name string, age int) error {
if name == "" {
return &ValidationError{
Field: "name",
Message: "bo‘sh bo‘lmasligi kerak",
}
}
if age < 0 {
return &ValidationError{
Field: "age",
Message: "manfiy bo‘lmasligi kerak",
}
}
return nil
}
func register(name string, age int) error {
if err := userYarat(name, age); err != nil {
return fmt.Errorf("foydalanuvchini ro‘yxatdan o‘tkazish: %w", err)
}
return nil
}
func main() {
err := register("", 20)
if err == nil {
return
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
fmt.Printf("%s maydonida xato: %s\n", validationErr.Field, validationErr.Message)
return
}
fmt.Println("Kutilmagan xato:", err)
}
Natija:
ValidationError Error() metodiga ega bo‘lgani uchun error interfeysini bajaradi. register() uni %w bilan o‘ragan. errors.As() zanjir ichidan *ValidationError turini topib, uning Field va Message maydonlarini ishlatish imkonini berdi.
Qisqacha farq:
errors.Is()ma’lum xato qiymati yoki sabab mavjudligini tekshiradi;errors.As()ma’lum xato turini topadi va uning ma’lumotlarini olishga yordam beradi.
defer va resurslarni yopish
defer funksiya tugashidan oldin bajariladigan chaqiruvni rejalashtiradi. U fayl kabi ochilgan resursni barcha chiqish yo‘llarida yopish uchun qulay:
package main
import (
"fmt"
"os"
)
func faylHajmi(nom string) (int64, error) {
file, err := os.Open(nom)
if err != nil {
return 0, fmt.Errorf("faylni ochish: %w", err)
}
defer file.Close()
info, err := file.Stat()
if err != nil {
return 0, fmt.Errorf("fayl ma’lumotini olish: %w", err)
}
return info.Size(), nil
}
func main() {
size, err := faylHajmi("config.json")
if err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println("Fayl hajmi:", size)
}
defer file.Close() faqat fayl muvaffaqiyatli ochilgandan keyin yozildi. file.Stat() xato qaytarsa ham, funksiya tugashida fayl yopiladi.
Bir nechta defer oxirgi qo‘shilganidan boshlab teskari tartibda bajariladi. Ularni uzoq sikl ichida ko‘p marta yig‘ish resurslarning funksiya tugaguncha ochiq qolishiga olib kelishi mumkin.
panic va recover
panic oddiy xatoni qaytarish usuli emas. U dastur odatiy davom eta olmaydigan holat yoki dasturchi xatosi uchun ishlatiladi. Fayl topilmasligi, noto‘g‘ri foydalanuvchi kiritishi va tarmoq uzilishi kabi kutiladigan holatlar error bilan qaytariladi.
recover() deferred funksiya ichida shu goroutine’dagi panic tarqalishini to‘xtata oladi:
package main
import "fmt"
func xavfsizIshgaTushir(fn func()) {
defer func() {
if value := recover(); value != nil {
fmt.Println("Panic ushlandi:", value)
}
}()
fn()
}
func main() {
xavfsizIshgaTushir(func() {
panic("kutilmagan ichki holat")
})
}
Natija:
recover() bajarilishni panic qatoridan davom ettirmaydi. Panic yuz bergan funksiya yakunlanadi, boshqaruv esa panicni ushlagan deferred funksiya tugagach yuqori qatlamga qaytadi.
Kutubxona yoki odatiy biznes funksiyasida panicni error o‘rniga ishlatmang. recover() ham xatolarni yashirish uchun emas, masalan serverdagi bitta noto‘g‘ri so‘rov butun jarayonni to‘xtatmasligi uchun eng yuqori chegarada qo‘llanadi.
Xatolar bilan ishlash tartibi
Amalda quyidagi tartib foydali:
- Xato yuz berishi mumkin bo‘lgan funksiya
errorqaytarsin. - Xatoni shu qatlam hal qila olsa, mos qaror qabul qilsin.
- Hal qila olmasa,
%wbilan foydali kontekst qo‘shib yuqoriga qaytarsin. - Chaqiruvchi alohida holatni ajratishi kerak bo‘lsa,
errors.Is()yokierrors.As()ishlatsin. - Xatoni foydalanuvchiga ko‘rsatish yoki logga yozishni tizim chegarasida bir marta bajaring.
panicni kutiladigan xatolar uchun ishlatmang.
Xulosa
- Faqat muvaffaqiyatsiz tugashi mumkin bo‘lgan funksiyalar odatda
errorqaytaradi. err != nilbo‘lsa, xatoni qaytarish yoki shu joyda mazmunli boshqarish kerak.fmt.Errorf()va%wxatoga kontekst qo‘shib, asl sababni saqlaydi.errors.Is()xato sababini,errors.As()esa maxsus xato turini tekshiradi.deferochilgan resurslarni ishonchli yopishga yordam beradi.panic/recoveroddiyerroroqimining o‘rnini bosmaydi.
Keyingi darsda xatoni stderr’ga yozish va mos exit code qaytarishni buyruq qatori dasturi misolida ko‘ramiz.