重學(xué)Go語(yǔ)言之錯(cuò)誤處理與異常機(jī)制詳解
作為開(kāi)發(fā)者來(lái)說(shuō),我們沒(méi)辦法保證程序在運(yùn)行過(guò)程中永遠(yuǎn)不會(huì)出現(xiàn)異常,對(duì)于異常,在很多編程語(yǔ)言中,可以用 try-catch語(yǔ)句來(lái)捕獲,而Go語(yǔ)言的開(kāi)發(fā)者顯然覺(jué)得 try-catch被濫用了,因此 Go不支持使用 try-catch語(yǔ)句捕獲異常處理。
那么,Go語(yǔ)言是如何定義和處理程序的異常呢?
Go語(yǔ)言的將程序運(yùn)行出現(xiàn)的問(wèn)題分為兩種:錯(cuò)誤(error)和異常(exception),錯(cuò)誤是程序(比如一個(gè)函數(shù))運(yùn)行的預(yù)期結(jié)果之一,當(dāng)你調(diào)用一個(gè)函數(shù)時(shí),就應(yīng)該處理出現(xiàn)錯(cuò)誤的情況,而異常是不可預(yù)期的(當(dāng)然也可以手動(dòng)觸發(fā))且會(huì)導(dǎo)致程序中斷運(yùn)行的嚴(yán)重錯(cuò)誤。
Go錯(cuò)誤處理策略
編寫(xiě)Go語(yǔ)言程序,一般推薦通過(guò)函數(shù)的最后一個(gè)返回值告訴調(diào)用者函數(shù)是否執(zhí)行成功,可以分兩種情況來(lái)討論:
第一種情況,如果函數(shù)的執(zhí)行結(jié)果只有正確與失敗,那么返回值可以是 boolean類(lèi)型:
func exists(key string) bool {
//to check if the key is exists
return true
}
if ok := exists("test"); ok {
// do something
}第二種情況,如果要讓調(diào)用者得到更詳細(xì)的錯(cuò)誤信息,顯然只返回一個(gè)布爾值是不夠的,這時(shí)候可以返回一個(gè) error類(lèi)型,error類(lèi)型是一個(gè)接口,只有一個(gè) Error方法的接口:
type error interface {
Error() string
}通過(guò) error類(lèi)型的 Error方法,可以獲得更詳細(xì)的錯(cuò)誤信息,方便調(diào)用者做進(jìn)一步的處理,因此在 Go標(biāo)準(zhǔn)庫(kù)的方法和函數(shù)中,一般都會(huì)將 error類(lèi)型作為最后一個(gè)返回值,比如我們以前文章中講到的 os包下的 Open和 Create函數(shù):
package os
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
func Create(name string) (*File, error) {
return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}當(dāng)函數(shù)最后一個(gè)返回值 error不為 nil時(shí),表示執(zhí)行不成功,此時(shí)其他的返回值就應(yīng)該忽略:
file,err := os.Open("./my.dat")
if err != nil{
return err
}
defer file.Close()上面代碼中,如果 error不為 nil,那么變量 file則為 nil,表示無(wú)法獲得一個(gè)文件句柄,這時(shí)候直接返回錯(cuò)誤,因?yàn)槿绻倮^續(xù)往下執(zhí)行,可能會(huì)引發(fā)程序崩潰。
當(dāng)然并不是所有情況下一遇到返回的 error不為 nil,就要拋棄其他返回值,比如使用 Read()方法讀取文件時(shí):
Read(p []byte) (n int, err error)
在讀取文件到結(jié)尾時(shí) Read方法會(huì)返回一個(gè) io.EOF的錯(cuò)誤:
var EOF = errors.New("EOF")此時(shí)雖然 error不為 nil,但仍然應(yīng)該把讀取到的字節(jié)數(shù)組保存,而不是拋棄掉。
創(chuàng)建error的幾種方式
當(dāng)我們開(kāi)發(fā)自己的函數(shù)時(shí),也可以創(chuàng)建自己的錯(cuò)誤類(lèi)型,有以下幾種方式:
errors包
errors包下的 New()函數(shù)可以創(chuàng)建一個(gè)只有文本信息的 error類(lèi)型:
func getFile(name string)(*os.file,error){
if name == ""{
return nil,errors.New("file name could not be empty")
}
}fmt包
fmt包下的 Errorf函數(shù)可以將文本格式后作為error類(lèi)型的錯(cuò)誤信息,并返回一個(gè)error類(lèi)型,因此其作用與errors.New函數(shù)類(lèi)似
func getFile(name string)(*os.file,error){
if name == ""{
return nil,fmt.Errorf("file name could not be empty")
}
}fmt.Errorf函數(shù)還能封裝其他 error類(lèi)型,再返回一個(gè)新的 error類(lèi)型,以此形成一條完整的錯(cuò)誤鏈條:
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}自定義錯(cuò)誤類(lèi)型
其實(shí),上面兩種創(chuàng)建錯(cuò)誤類(lèi)型的方式,本質(zhì)上都是實(shí)現(xiàn) error接口,我們也可以創(chuàng)建一個(gè)擁有 Error方法的類(lèi)型來(lái)實(shí)現(xiàn) error接口:
type Result struct {
Code int
Message string
Data interface{}
}
func (r Result) Error() string {
return r.Message
}如何處理錯(cuò)誤
當(dāng)調(diào)用的函數(shù)或方法的返回值有 error類(lèi)型時(shí),最簡(jiǎn)單的當(dāng)然可以選擇直接忽略錯(cuò)誤,不過(guò)更恰當(dāng)?shù)姆绞绞翘幚韺?duì)應(yīng)的錯(cuò)誤,有以下幾種處理策略:
直接返回錯(cuò)誤
對(duì)于函數(shù)來(lái)說(shuō),如果在執(zhí)行時(shí)遇到錯(cuò)誤,可以直接返回給上層調(diào)用者:
func SendMessage(url string) error {
if url == ""{
return errors.New("url can't not be empty")
}
_, err := http.Get(url)
if err != nil {
return err
}
return nil
}記錄日志并繼續(xù)運(yùn)行
當(dāng)調(diào)用函數(shù)時(shí)遇到返回的錯(cuò)誤,如果不影響程序運(yùn)行,也可以選擇記錄錯(cuò)誤日志并往下執(zhí)行:
if err := SendMessage("https://xxx/sendMessage");err != nil{
log.Printf("the message sending been broken by : %v\n", err)
}記錄日志并結(jié)束運(yùn)行
如果錯(cuò)誤影響程序的執(zhí)行,也可以記錄日志后,退出程序執(zhí)行:
if err := SendMessage("https://xxx/sendMessage");err != nil{
log.Printf("the message sending been broken by : %v\n", err)
os.Exit(1)
}記錄日志并退出執(zhí)行,直接用 log包的 Fatalf函數(shù)就可以做到,因此上面代碼的更簡(jiǎn)潔做法是:
if err := SendMessage("https://xxx/sendMessage");err != nil{
log.Fatalf("the message sending been broken by : %v\n", err)
}Go異常處理機(jī)制
在Go語(yǔ)言中,異常是指會(huì)引發(fā)程序崩潰無(wú)法繼續(xù)運(yùn)行的錯(cuò)誤,比如數(shù)組越界或者空指針引用等情況,這時(shí)候會(huì)觸發(fā) panic異常:
package main
func main() {
var s = []int{1, 2, 3}
s[3] = 10
}上面程序運(yùn)行結(jié)果如下,可以看出觸發(fā)了數(shù)組越界的異常:
panic: runtime error: index out of range [3] with length 3
無(wú)論是在主協(xié)程還是子協(xié)程中,一旦觸發(fā) panic異常,整個(gè)程序都會(huì)終止運(yùn)行。
panic函數(shù)
除了數(shù)組越界等不可預(yù)測(cè)的異常會(huì)自動(dòng)觸發(fā) panic,也可以手動(dòng)調(diào)用 panic函數(shù)觸發(fā)異常來(lái)終止程序的運(yùn)行:
func loadConfig(path string){
panic("can't load the config file on path " + path)
}一個(gè)良好的程序最好不要主動(dòng)調(diào)用 panic函數(shù),尤其是開(kāi)發(fā)類(lèi)庫(kù)的時(shí)候,最好通過(guò)返回 error類(lèi)型來(lái)告訴調(diào)用者發(fā)生了什么錯(cuò)誤,而不是觸發(fā) panic導(dǎo)致程序終止運(yùn)行。
recover函數(shù)
當(dāng)發(fā)生 panic異常時(shí),如果不捕獲得異常,那么程序就是終止運(yùn)行,在Go語(yǔ)言中,可以用 defer語(yǔ)句和 recover函數(shù)的模式來(lái)捕獲 panic異常:
package main
import (
"fmt"
"log"
)
func main() {
n1 := FindElementByIndex(1)
fmt.Println(n1)
n2 := FindElementByIndex(4)
fmt.Println(n2)
}
func FindElementByIndex(index int) int {
defer func() {
if e := recover(); e != nil {
log.Fatal(e)
}
}()
s := []int{1, 2, 3, 4}
return s[index]
}小結(jié)
調(diào)用函數(shù)時(shí),錯(cuò)誤是執(zhí)行的結(jié)果,應(yīng)該及時(shí)處理,而異常往往不可控,可以用defer+recover的模式進(jìn)行捕獲。
到此這篇關(guān)于重學(xué)Go語(yǔ)言之錯(cuò)誤處理與異常機(jī)制詳解的文章就介紹到這了,更多相關(guān)Go錯(cuò)誤處理與異常機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言協(xié)程處理數(shù)據(jù)有哪些問(wèn)題
協(xié)程(coroutine)是Go語(yǔ)言中的輕量級(jí)線(xiàn)程實(shí)現(xiàn),由Go運(yùn)行時(shí)(runtime)管理。本文為大家詳細(xì)介紹了Go中的協(xié)程,協(xié)程不需要搶占式調(diào)度,可以有效提高線(xiàn)程的任務(wù)并發(fā)性,而避免多線(xiàn)程的缺點(diǎn)2023-02-02
Go語(yǔ)言中三種容器類(lèi)型的數(shù)據(jù)結(jié)構(gòu)詳解
在?Go?語(yǔ)言中,有三種主要的容器類(lèi)型用于存儲(chǔ)和操作集合數(shù)據(jù)這篇文章主要為大家介紹了三者的使用與區(qū)別,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
Go語(yǔ)言基礎(chǔ)語(yǔ)法之結(jié)構(gòu)體及方法詳解
結(jié)構(gòu)體類(lèi)型可以用來(lái)保存不同類(lèi)型的數(shù)據(jù),也可以通過(guò)方法的形式來(lái)聲明它的行為。本文將介紹go語(yǔ)言中的結(jié)構(gòu)體和方法,以及“繼承”的實(shí)現(xiàn)方法2021-09-09
基于Golang實(shí)現(xiàn)YOLO目標(biāo)檢測(cè)算法
目標(biāo)檢測(cè)是計(jì)算機(jī)視覺(jué)領(lǐng)域的重要任務(wù),它不僅可以識(shí)別圖像中的物體,還可以標(biāo)記出物體的位置和邊界框,YOLO是一種先進(jìn)的目標(biāo)檢測(cè)算法,以其高精度和實(shí)時(shí)性而聞名,本文將介紹如何使用Golang實(shí)現(xiàn)YOLO目標(biāo)檢測(cè)算法,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下2023-11-11
Golang實(shí)現(xiàn)WebSocket服務(wù)的項(xiàng)目實(shí)踐
本文介紹如何使用Golang實(shí)現(xiàn)實(shí)時(shí)后端WebSocket服務(wù),首先使用Gin框架搭建http服務(wù),然后使用gorilla/websocket庫(kù)實(shí)現(xiàn)簡(jiǎn)單后端WebSocket服務(wù),具有一定的參考價(jià)值,感興趣的可以了解一下2023-05-05
golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例
這篇文章主要為大家介紹了golang?gorm錯(cuò)誤處理事務(wù)以及日志用法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04

