Defer — Go’ga xos, boshqa ko’p tillarda to’g’ridan-to’g’ri analogi bo’lmagan kalit so’z. U funksiya chaqiruvini hozir emas, o’zini o’rab turgan funksiya tugashidan oldin bajarilishga “kechiktiradi” — resurslarni tozalash (fayl yopish, ulanishni uzish) uchun ideal vosita.
defer —
deferbilan belgilangan funksiya chaqiruvi darhol emas, uni o’rab turgan funksiyareturnqilishidan bir zum oldin bajariladi. Argumentlar esadeferyozilgan paytning o’zida hisoblanadi.
Asosiy ishlash tartibi
package main
import "fmt"
func main() {
fmt.Println("Fayl ochildi")
defer fmt.Println("Fayl yopildi")
fmt.Println("Fayl bilan ishlanmoqda")
}
Fayl ochildi
Fayl bilan ishlanmoqda
Fayl yopildi
defer fmt.Println("Fayl yopildi") yozilgan joyida emas, main tugashidan oldin ishga tushdi — garchi u kod bo’yicha ikkinchi qatorda tursa ham, natijada oxirgi bo’lib chop etildi.
Bir nechta defer — teskari tartibda (LIFO)
Bitta funksiyada bir nechta defer bo’lsa, ular teskari — oxirgi e’lon qilingani birinchi bo’lib — ishga tushadi:
package main
import "fmt"
func main() {
fmt.Println("Boshlanishi")
defer fmt.Println("Birinchi defer")
defer fmt.Println("Ikkinchi defer")
defer fmt.Println("Uchinchi defer")
fmt.Println("Tugashi")
}
Boshlanishi
Tugashi
Uchinchi defer
Ikkinchi defer
Birinchi defer
Uchta defer steka (LIFO — Last In, First Out) tamoyili bilan yig’iladi: oxirgi qo’shilgani (“Uchinchi”) birinchi bajariladi. Bu, masalan, bir nechta resurs ochilganda, ularni ochilishga teskari tartibda yopish uchun tabiiy mos keladi.
Argument darhol hisoblanadi, chaqiruv esa kechiktiriladi
Nozik, lekin muhim jihat: deferga uzatilgan argumentning qiymati shu qatorning o’zida, hisoblanadi — funksiya esa keyinroq, o’sha “muzlatilgan” qiymat bilan chaqiriladi:
package main
import "fmt"
func main() {
son := 1
defer fmt.Println("Deferdagi qiymat:", son)
son = 100
fmt.Println("Joriy qiymat:", son)
}
Joriy qiymat: 100
Deferdagi qiymat: 1
defer fmt.Println(..., son) yozilgan paytda son — 1 edi, aynan shu qiymat “eslab qolindi”. Keyinroq son = 100 deb o’zgartirilgan bo’lsa-da, deferlangan chaqiruv baribir eski, 1 qiymatini ko’rsatdi.
Amaliy foydalanish — resursni yopish
deferning eng keng tarqalgan qo’llanilishi — resurs ochilgan joyning o’zida, uni yopish kodini ham darhol yozish, shunda uni unutib qo’yish deyarli mumkin emas:
package main
import "fmt"
func amalBajar() {
fmt.Println("Boshlash")
defer fmt.Println("Tozalash")
fmt.Println("1-qadam")
fmt.Println("2-qadam")
}
func main() {
amalBajar()
fmt.Println("Dastur tugadi")
}
Boshlash
1-qadam
2-qadam
Tozalash
Dastur tugadi
defer fmt.Println("Tozalash") funksiya boshida yozilgan bo’lsa-da, u amalBajar ichidagi barcha qadamlardan keyin, lekin amalBajar chaqiruvchiga qaytishidan oldin ishladi. Haqiqiy kodda bu o’rinda odatda fayl.Close() yoki ulanish.Yopish() kabi chaqiruv turadi.