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