panic ба recover
Go-д алдааг ихэвчлэн утга болгон буцаадаг гэж сурсан. Гэхдээ зарим нэг нөхцөл байдал бий — программ үргэлжлэн ажиллах боломжгүй болох гэнэтийн, хүнд нөхцөл. Тэр үед panic ба recover ажилладаг.
panic гэж юу вэ?
panic нь программыг нэн даруй зогсоодог функц юм. Суурилагдсан Go функцүүд заримдаа panic үүсгэдэг — жишээ нь slice-ын хязгаараас гарах үед:
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
fmt.Println(s[0]) // Зөв — 1
fmt.Println(s[5]) // panic: runtime error: index out of range [5] with length 3
}
Программ дараах мессежтэйгээр зогсоно:
panic: runtime error: index out of range [5] with length 3
goroutine 1 [running]:
main.main()
main.go:8 +0x...
panic дуудах
Та өөрөө panic дуудаж болно — гэхдээ маш хязгаарлагдмал нөхцөлд:
package main
import "fmt"
func mustPositive(n int) int {
if n <= 0 {
panic(fmt.Sprintf("n тэгээс их байх ёстой, харин %d ирлээ", n))
}
return n
}
func main() {
fmt.Println(mustPositive(5)) // 5
fmt.Println(mustPositive(-1)) // panic!
}
panic нь алдаа буцааж болохгүй нөхцөлд — жишээ нь программ эхлэхэд шаардлагатай тохиргоо дутсан үед — ашигладаг. Энгийн алдаа зохицуулалтад panic ашиглах нь буруу хэв маяг.
recover — panic-аас сэргээх
panic болоход программ зогсохоос өмнө defer функцүүд ажилладаг. Тэр defer дотор recover дуудаж panic-аас "аврах" боломжтой:
package main
import "fmt"
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic-аас сэргэлээ: %v", r)
}
}()
result = a / b // b == 0 бол panic!
return result, nil
}
func main() {
result, err := safeDiv(10, 2)
if err != nil {
fmt.Println("Алдаа:", err)
} else {
fmt.Println("Үр дүн:", result)
}
result, err = safeDiv(10, 0)
if err != nil {
fmt.Println("Алдаа:", err)
} else {
fmt.Println("Үр дүн:", result)
}
}
recover() нь nil бус утга буцаавал panic болсон гэсэн үг. Тэр утга нь panic(...) руу дамжуулсан аргумент байна.
defer ба panic-ийн гүйцэтгэлийн дараалал
Panic болоход defer-үүд LIFO (сүүлийнх эхлээд) дарааллаар ажилладаг:
package main
import "fmt"
func example() {
defer fmt.Println("defer 1 — эхлээд бүртгэгдсэн, хамгийн сүүлд ажиллана")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3 — хамгийн сүүлд бүртгэгдсэн, эхлээд ажиллана")
panic("туршилтын panic")
}
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Panic барилаа:", r)
}
}()
example()
fmt.Println("Энэ мөр хэзээ ч хэвлэгдэхгүй")
}
Гаралт:
defer 3 — хамгийн сүүлд бүртгэгдсэн, эхлээд ажиллана
defer 2
defer 1 — эхлээд бүртгэгдсэн, хамгийн сүүлд ажиллана
Panic барилаа: туршилтын panic
Хэзээ panic ашиглах вэ?
Go-ийн олон нийтийн зөвлөмж:
Энгийн алдаа → error утга буцаах
Хөгжүүлэлтийн алдаа → panic (жишээ нь nil pointer-ийг дамжуулах)
Программ эхлэх тохиргооны алдаа → panic
Ихэнх тохиолдолд panic ашиглах шаардлагагүй. Алдааг утга болгон буцаах нь Go-ийн гол зарчим юм.
Дараагийн хичээлд:
Go-ийн хамгийн ялгарах онцлог болох goroutine — нэгэн зэрэг олон ажил гүйцэтгэх хөнгөн механизмыг судална. Энэ нь Go-г backend болон systems programming-д тэргүүлэгч болгосон гол шалтгаан юм.