Go 分布式学习利器(14)-- Go语言的错误处理
1. Go 的錯(cuò)誤機(jī)制
Go 語言的錯(cuò)誤機(jī)制中與其他語言的主要差異如下:
- 沒有異常機(jī)制
- error 類型實(shí)現(xiàn)了 error接口
type error interface {Error() string } - 可以通過errors.New來快速創(chuàng)建錯(cuò)誤實(shí)例
errors.New(" num is not in range[0,100]")
如下測(cè)試代碼演示基本的錯(cuò)誤處理:
package error_testimport ("errors""testing"
)/* 斐波那契數(shù)列 */
func GetFibList(n int) ([]int,error){ // 兩個(gè)返回值if n < 2 || n > 100 { // 返回錯(cuò)誤return nil, errors.New("n should in range [2..100]")}fibList := []int{1,1}for i := 2; i < n; i++ {fibList = append(fibList, fibList[i-1] + fibList[i-2])}return fibList,nil
}func TestFibArray(t *testing.T) {if v,err := GetFibList(-10);err != nil {t.Error(err)} else {t.Log(v)}
}
輸出如下:
=== RUN TestFibArrayerror_test.go:24: n should in range [2..100]
--- FAIL: TestFibArray (0.00s)
如果我們需要有多種錯(cuò)誤類型的返回,那么就需要區(qū)分不同的錯(cuò)誤,可以通過設(shè)置預(yù)置的錯(cuò)誤類型來 快速簡(jiǎn)便得區(qū)分不同的錯(cuò)誤
package error_testimport ("errors""testing"
)/*區(qū)分不同的錯(cuò)誤類型*/
var LessThanTwo = errors.New("n should large than 2")
var BiggerThanHundred = errors.New("n should small than 100")/* 斐波那契數(shù)列 */
func GetFibList(n int) ([]int,error){ /* 兩個(gè)返回值 */if n < 2 {return nil, LessThanTwo}if n > 100 {return nil, BiggerThanHundred}fibList := []int{1,1}for i := 2; i < n; i++ {fibList = append(fibList, fibList[i-1] + fibList[i-2])}return fibList,nil
}func TestFibArray(t *testing.T) {if v,err := GetFibList(-10);err != nil {t.Error(err)} else {t.Log(v)}
}
輸出如下:
=== RUN TestFibArrayerror_test.go:32: n should large than 2
--- FAIL: TestFibArray (0.00s)
2. 錯(cuò)誤處理的最佳實(shí)踐
按照我們以上代碼的處理邏輯,我們能夠發(fā)現(xiàn)函數(shù)有多個(gè)返回值的時(shí)候 如果每個(gè)返回值都需要判斷,并作出對(duì)應(yīng)的錯(cuò)誤處理,這回讓代碼非常冗余。
所以一般針對(duì)有多個(gè)錯(cuò)誤 返回值的多個(gè)函數(shù),會(huì)直接判斷錯(cuò)誤返回值不為空,直到調(diào)用到最終正常的結(jié)果才返回。
如下函數(shù)針對(duì)多個(gè)函數(shù)的多個(gè)錯(cuò)誤返回值的正常處理:
func GetFibList2( str string) {var (i interr errorlist []int)if i, err = strconv.Atoi(str); err != nil { // 錯(cuò)誤輸出fmt.Println("Atoi error", err)return}if list,err = GetFibList(i); err != nil{ // 錯(cuò)誤輸出fmt.Println("GetFibList error",err)return}fmt.Println(list) // 直到所有函數(shù)都執(zhí)行正常才最終輸出
}
3. panic和recover
panic關(guān)鍵字是Go語言中程序異常退出的關(guān)鍵字
- 用于不可以恢復(fù)的錯(cuò)誤
- panic 程序退出前 會(huì)執(zhí)行 defer指定的函數(shù),并打印函數(shù)調(diào)用棧
這里先對(duì)比一下panic和 os.Exit 兩個(gè)異常退出函數(shù)的區(qū)別
os.Exit退出時(shí)不會(huì)調(diào)用defer函數(shù)os.Exit退出時(shí)不會(huì)打印函數(shù)調(diào)用棧信息
如下測(cè)試代碼:
package errorimport ("errors""fmt""testing"
)func TestPanicVsExit(t *testing.T){defer func(){fmt.Println("finally execute")}()fmt.Println("start")// os.Exit(-1)panic(errors.New("something wrong"))
}
輸出如下,可以看到panic能夠打印調(diào)用棧和執(zhí)行defer函數(shù)
=== RUN TestPanicVsExit
start
finally execute
--- FAIL: TestPanicVsExit (0.00s)
panic: something wrong [recovered]panic: something wronggoroutine 18 [running]:
testing.tRunner.func1.1(0x11199c0, 0xc00008e4a0)/usr/local/Cellar/go/1.14.6/libexec/src/testing/testing.go:988 +0x30d
testing.tRunner.func1(0xc0000ce120)/usr/local/Cellar/go/1.14.6/libexec/src/testing/testing.go:991 +0x3f9
panic(0x11199c0, 0xc00008e4a0)/usr/local/Cellar/go/1.14.6/libexec/src/runtime/panic.go:969 +0x166
command-line-arguments.TestPanicVsExit(0xc0000ce120)/Users/zhanghuigui/go/src/test/ch10/error_test/panic_test.go:17 +0xd7
testing.tRunner(0xc0000ce120, 0x114b278)/usr/local/Cellar/go/1.14.6/libexec/src/testing/testing.go:1039 +0xdc
created by testing.(*T).Run/usr/local/Cellar/go/1.14.6/libexec/src/testing/testing.go:1090 +0x372
FAIL command-line-arguments 0.663s
而os.Exit(-1)則直接退出程序。
recover 關(guān)鍵字類似于我們C++/Java中 的try..catch關(guān)鍵字,能夠防止程序異常后不會(huì)退出,而執(zhí)行自己想要做的異常恢復(fù)或者對(duì)應(yīng)的錯(cuò)誤處理。
Go語言通過recover完成類似其他編譯型語言的錯(cuò)誤恢復(fù)處理。
如下簡(jiǎn)單測(cè)試代碼:
package errorimport ("errors""fmt""testing"
)func TestPanicVsExit(t *testing.T){defer func(){if err := recover(); err != nil { // 并沒有做異常恢復(fù),僅僅是把異常打出來fmt.Println("recover : ", err)}}()fmt.Println("start")panic(errors.New("something wrong")) // 通過panic 傳入異常
}
最后的輸出比較有趣,可以看到panic并沒有讓程序異常退出和打程序調(diào)用棧,異常錯(cuò)誤直接被recover捕獲,程序執(zhí)行狀態(tài)是PASS
=== RUN TestPanicVsExit
start
recover from something wrong
--- PASS: TestPanicVsExit (0.00s)
最常見的“錯(cuò)誤恢復(fù)” 如下, 是不被建議使用的
defer func(){if err := recover(); err != nil { // 并沒有做異常恢復(fù),僅僅是把異常打出來log.Error("recovered panic",err)}}()
這樣的處理錯(cuò)誤僅僅是把異常結(jié)果打印到日志中或者直接忽略掉,這個(gè)時(shí)候可能一些錯(cuò)誤是系統(tǒng)異常造成的,容易形成僵尸進(jìn)程。
即如果一些異常是系統(tǒng)性異常(內(nèi)存分配異常,磁盤異常。。。等),可以直接讓進(jìn)程崩潰,并將該異常告知開發(fā)者,由他決定如何處理。
總結(jié)
以上是生活随笔為你收集整理的Go 分布式学习利器(14)-- Go语言的错误处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个时姓好听的名字
- 下一篇: Go 分布式学习利器(15) -- Go