錯(cuò)誤
在Go中有一部分函數(shù)總是能成功的運(yùn)行。比如strings.Contains和strconv.FormatBool函數(shù);對(duì)于大部分函數(shù)而言,永遠(yuǎn)無(wú)法確保能否成功運(yùn)行。
Go 語(yǔ)言通過(guò)內(nèi)置的錯(cuò)誤接口提供了非常簡(jiǎn)單的錯(cuò)誤處理機(jī)制。
error類型是一個(gè)接口類型,這是它的定義:
type error interface { Error() string}
我們可以在編碼中通過(guò)實(shí)現(xiàn) error 接口類型來(lái)生成錯(cuò)誤信息。
函數(shù)通常在最后的返回值中返回錯(cuò)誤信息。使用errors.New 可返回一個(gè)錯(cuò)誤信息:
func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 實(shí)現(xiàn)}
這里有一個(gè)錯(cuò)誤處理的例子:
package mainimport “errors”import “fmt”// 按照慣例,錯(cuò)誤通常是最后一個(gè)返回值并且是 error 類型,一個(gè)內(nèi)建的接口。func f1(arg int) (int, error) {// errors.New 構(gòu)造一個(gè)使用給定的錯(cuò)誤信息的基本error 值。if arg == 42 {return -1, errors.New(“can’t work with 42”)}// 返回錯(cuò)誤值為 nil 代表沒(méi)有錯(cuò)誤。return arg + 3, nil}// 通過(guò)實(shí)現(xiàn) Error 方法來(lái)自定義 error 類型是可以的。// 這里使用自定義錯(cuò)誤類型來(lái)表示上面的參數(shù)錯(cuò)誤。type argError struct {arg intprob string}func (e *argError) Error() string {return fmt.Sprintf(“%d – %s”, e.arg, e.prob)}func f2(arg int) (int, error) {if arg == 42 {// 在這個(gè)例子中,我們使用 &argError 語(yǔ)法來(lái)建立一個(gè)新的結(jié)構(gòu)體,并提供了 arg 和 prob 這個(gè)兩個(gè)字段的值。return -1, &argError{arg, “can’t work with it”}}return arg + 3, nil}func main() {// 下面的兩個(gè)循環(huán)測(cè)試了各個(gè)返回錯(cuò)誤的函數(shù)。// 注意在 if行內(nèi)的錯(cuò)誤檢查代碼,在 Go 中是一個(gè)普遍的用法。for _, i := range []int{7, 42} {if r, e := f1(i); e != nil {fmt.Println(“f1 失敗:”, e)} else {fmt.Println(“f1 工作:”, r)}}for _, i := range []int{7, 42} {if r, e := f2(i); e != nil {fmt.Println(“f2 失敗:”, e)} else {fmt.Println(“f2 工作:”, r)}}// 你如果想在程序中使用一個(gè)自定義錯(cuò)誤類型中的數(shù)據(jù),你需要通過(guò)類型斷言來(lái)得到這個(gè)錯(cuò)誤類型的實(shí)例。_, e := f2(42)if ae, ok := e.(*argError); ok {fmt.Println(ae.arg)fmt.Println(ae.prob)}}
Deferred函數(shù)
defer語(yǔ)句經(jīng)常被用于處理成對(duì)的操作,如打開(kāi)、關(guān)閉、連接、斷開(kāi)連接、加鎖、釋放鎖。通過(guò)defer機(jī)制,不論函數(shù)邏輯多復(fù)雜,都能保證在任何執(zhí)行路徑下,資源被釋放。釋放資源的defer應(yīng)該直接跟在請(qǐng)求資源的語(yǔ)句后。
示例:
package mainimport “fmt”import “os”func main() {// 假設(shè)我們想要?jiǎng)?chuàng)建一個(gè)文件,向它進(jìn)行寫(xiě)操作,然后在結(jié)束時(shí)關(guān)閉它。// 這里展示了如何通過(guò) defer 來(lái)做到這一切。f := createFile(“D:/defer.txt”) // f := createFile(“/tmp/defer.txt”)// 在 closeFile 后得到一個(gè)文件對(duì)象,我們使用 defer通過(guò) closeFile 來(lái)關(guān)閉這個(gè)文件。這會(huì)在封閉函數(shù)(main)結(jié)束時(shí)執(zhí)行,就是 writeFile 結(jié)束后。defer closeFile(f)writeFile(f)}func createFile(p string) *os.File {fmt.Println(“creating”)f, err := os.Create(p)if err != nil {panic(err)}return f}func writeFile(f *os.File) {fmt.Println(“writing”)fmt.Fprintln(f, “data”)}func closeFile(f *os.File) {fmt.Println(“closing”)f.Close()}
異常
Go的類型系統(tǒng)會(huì)在編譯時(shí)捕獲很多錯(cuò)誤,但有些錯(cuò)誤只能在運(yùn)行時(shí)檢查,如數(shù)組訪問(wèn)越界、空指針引用等。這些運(yùn)行時(shí)錯(cuò)誤會(huì)引起painc異常。
示例如下:
package mainimport (“fmt””os”)func main() {// 我們將在這個(gè)網(wǎng)站中使用 panic 來(lái)檢查預(yù)期外的錯(cuò)誤。這個(gè)是唯一一個(gè)為 panic 準(zhǔn)備的例子。panic(“一個(gè)異常”)// panic 的一個(gè)基本用法就是在一個(gè)函數(shù)返回了錯(cuò)誤值但是我們并不知道(或者不想)處理時(shí)終止運(yùn)行。// 這里是一個(gè)在創(chuàng)建一個(gè)新文件時(shí)返回異常錯(cuò)誤時(shí)的panic 用法。fmt.Println(“繼續(xù)”)_, err := os.Create(“/tmp/file”)if err != nil {panic(err)}// 運(yùn)行程序?qū)?huì)引起 panic,輸出一個(gè)錯(cuò)誤消息和 Go 運(yùn)行時(shí)棧信息,并且返回一個(gè)非零的狀態(tài)碼。}
捕獲異常
通常來(lái)說(shuō),不應(yīng)該對(duì)panic異常做任何處理,但有時(shí),也許我們可以從異常中恢復(fù),至少我們可以在程序崩潰前,做一些操作。舉個(gè)例子,當(dāng)web服務(wù)器遇到不可預(yù)料的嚴(yán)重問(wèn)題時(shí),在崩潰前應(yīng)該將所有的連接關(guān)閉;如果不做任何處理,會(huì)使得客戶端一直處于等待狀態(tài)。如果web服務(wù)器還在開(kāi)發(fā)階段,服務(wù)器甚至可以將異常信息反饋到客戶端,幫助調(diào)試。
如果在deferred函數(shù)中調(diào)用了內(nèi)置函數(shù)recover,并且定義該defer語(yǔ)句的函數(shù)發(fā)生了panic異常,recover會(huì)使程序從panic中恢復(fù),并返回panic value。導(dǎo)致panic異常的函數(shù)不會(huì)繼續(xù)運(yùn)行,但能正常返回。在未發(fā)生panic時(shí)調(diào)用recover,recover會(huì)返回nil。
示例:
package mainimport “fmt”func main() {// 這里我們對(duì)異常進(jìn)行了捕獲defer func() {if p := recover(); p != nil {err := fmt.Errorf(“internal error: %v”, p)if err != nil {fmt.Println(err)}}}()// 我們將在這個(gè)網(wǎng)站中使用 panic 來(lái)檢查預(yù)期外的錯(cuò)誤。這個(gè)是唯一一個(gè)為 panic 準(zhǔn)備的例子。panic(“一個(gè)異常”)}
分享不易,歡迎大家點(diǎn)贊關(guān)注~~~ 感謝大家閱讀!