Go語(yǔ)言錯(cuò)誤和異常實(shí)現(xiàn)
Go 語(yǔ)言沒(méi)有像 Java 或 Python 那樣的 try-catch 異常捕獲機(jī)制,而是采用了一種顯式的錯(cuò)誤處理方式,即通過(guò)返回 error 類(lèi)型來(lái)表示可能發(fā)生的錯(cuò)誤。此外,Go 還提供了 panic 和 recover 機(jī)制用于處理程序運(yùn)行時(shí)的嚴(yán)重錯(cuò)誤。
一、Go 錯(cuò)誤處理機(jī)制概述
在 Go 語(yǔ)言中,錯(cuò)誤是可以預(yù)期的,并且不是非常嚴(yán)重,不會(huì)影響程序的運(yùn)行。對(duì)于這類(lèi)問(wèn)題,可以用返回錯(cuò)誤給調(diào)用者的方法,讓調(diào)用者自己決定如何處理。
Go 語(yǔ)言推薦使用 多返回值 的方式返回錯(cuò)誤,通常函數(shù)的最后一個(gè)返回值是 error 類(lèi)型。調(diào)用者需要檢查這個(gè)返回值是否為 nil,來(lái)判斷是否發(fā)生了錯(cuò)誤。
1.1 error 類(lèi)型
在 Go 語(yǔ)言中,錯(cuò)誤是通過(guò)內(nèi)置的 error 接口表示的。它非常簡(jiǎn)單,只有一個(gè) Error 方法用來(lái)返回具體的錯(cuò)誤信息,定義如下:
type error interface {
Error() string
}
任何實(shí)現(xiàn)了 Error() string 方法的類(lèi)型都可以作為 error 使用。在下面的代碼中,我演示了一個(gè)字符串轉(zhuǎn)整數(shù)的例子:
func main() {
i,err:=strconv.Atoi("a")
if err!=nil {
fmt.Println(err)
}else {
fmt.Println(i)
}
}
這里我故意使用了字符串 “a”,嘗試把它轉(zhuǎn)為整數(shù)。我們知道 “a” 是無(wú)法轉(zhuǎn)為數(shù)字的,所以運(yùn)行這段程序,會(huì)打印出如下錯(cuò)誤信息:
strconv.Atoi: parsing "a": invalid syntax
這個(gè)錯(cuò)誤信息就是通過(guò)接口 error 返回的。我們來(lái)看關(guān)于函數(shù) strconv.Atoi 的定義,如下所示:
func Atoi(s string) (int, error)
一般而言,error 接口用于當(dāng)方法或者函數(shù)執(zhí)行遇到錯(cuò)誤時(shí)進(jìn)行返回,而且是第二個(gè)返回值。通過(guò)這種方式,可以讓調(diào)用者自己根據(jù)錯(cuò)誤信息決定如何進(jìn)行下一步處理。
1.2errors.New()和fmt.Errorf()
除了可以使用其他函數(shù),自己定義的函數(shù)也可以返回錯(cuò)誤信息給調(diào)用者。Go 提供了 errors.New() 和 fmt.Errorf() 來(lái)創(chuàng)建錯(cuò)誤。基本錯(cuò)誤處理示例代碼:
package main
import (
"errors"
"fmt"
)
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Result:", result)
}
輸出:
Error: division by zero
Go 1.13 引入了錯(cuò)誤包裝(Error Wrapping)機(jī)制,可以使用 fmt.Errorf 和 %w 動(dòng)詞將一個(gè)錯(cuò)誤包裝進(jìn)另一個(gè)錯(cuò)誤中,并保留原始錯(cuò)誤信息。
錯(cuò)誤包裝示例代碼:
package main
import (
"errors"
"fmt"
)
func openFile(filename string) error {
return errors.New("file not found")
}
func readFile(filename string) error {
err := openFile(filename)
if err != nil {
return fmt.Errorf("readFile failed: %w", err)
}
return nil
}
func main() {
err := readFile("data.txt")
if err != nil {
fmt.Println("Error:", err)
if errors.Is(err, errors.New("file not found")) {
fmt.Println("It's a file not found error!")
}
}
}
輸出:
Error: readFile failed: file not found
It's a file not found error!
1.3 自定義錯(cuò)誤類(lèi)型
我們可以通過(guò)實(shí)現(xiàn) error 接口來(lái)定義自己的錯(cuò)誤類(lèi)型,這樣可以攜帶更多上下文信息。自定義錯(cuò)誤類(lèi)型示例代碼:
package main
import "fmt"
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}
func checkAge(age int) error {
if age < 18 {
return &MyError{Code: 403, Message: "Access denied: underage"}
}
return nil
}
func main() {
err := checkAge(16)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Access granted")
}
輸出:
Error 403: Access denied: underage
二、panic 和 recover
Go 提供了 panic 和 recover 用于處理程序運(yùn)行時(shí)的嚴(yán)重錯(cuò)誤(如數(shù)組越界、空指針解引用等)。panic 會(huì)中斷程序執(zhí)行,而 recover 可以捕獲 panic,避免程序崩潰。
2.1 panic 的使用
panic 用于表示程序遇到了無(wú)法繼續(xù)執(zhí)行的嚴(yán)重錯(cuò)誤。panic示例代碼:
package main
import "fmt"
func testPanic() {
panic("Something went wrong!")
}
func main() {
fmt.Println("Start")
testPanic()
fmt.Println("End") // 不會(huì)執(zhí)行
}
輸出:
Start
panic: Something went wrong!
goroutine 1 [running]:
main.testPanic()
/tmp/sandbox123/prog.go:6 +0x39
main.main()
/tmp/sandbox123/prog.go:10 +0x65
2.2 recover 的使用
recover 必須在 defer 中調(diào)用,用于捕獲 panic,避免程序崩潰。recover示例代碼:
package main
import "fmt"
func safeExecute() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
panic("Oops! Panic occurred")
}
func main() {
fmt.Println("Start")
safeExecute()
fmt.Println("End")
}
輸出:
Start
Recovered from panic: Oops! Panic occurred
End
三、Deferred 函數(shù)
在一個(gè)自定義函數(shù)中,你打開(kāi)了一個(gè)文件,然后需要關(guān)閉它以釋放資源。不管你的代碼執(zhí)行了多少分支,是否出現(xiàn)了錯(cuò)誤,文件是一定要關(guān)閉的,這樣才能保證資源的釋放。
如果這個(gè)事情由開(kāi)發(fā)人員來(lái)做,隨著業(yè)務(wù)邏輯的復(fù)雜會(huì)變得非常麻煩,而且還有可能會(huì)忘記關(guān)閉。基于這種情況,Go 語(yǔ)言為我們提供了 defer 函數(shù),可以保證文件關(guān)閉后一定會(huì)被執(zhí)行,不管你自定義的函數(shù)出現(xiàn)異常還是錯(cuò)誤。
下面的代碼是 Go 語(yǔ)言標(biāo)準(zhǔn)包 ioutil 中的 ReadFile 函數(shù),它需要打開(kāi)一個(gè)文件,然后通過(guò) defer 關(guān)鍵字確保在 ReadFile 函數(shù)執(zhí)行結(jié)束后,f.Close() 方法被執(zhí)行,這樣文件的資源才一定會(huì)釋放。
func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
//省略無(wú)關(guān)代碼
return readAll(f, n)
}
defer 關(guān)鍵字用于修飾一個(gè)函數(shù)或者方法,使得該函數(shù)或者方法在返回前才會(huì)執(zhí)行,也就說(shuō)被延遲,但又可以保證一定會(huì)執(zhí)行。
以上面的 ReadFile 函數(shù)為例,被 defer 修飾的 f.Close 方法延遲執(zhí)行,也就是說(shuō)會(huì)先執(zhí)行 readAll(f, n),然后在整個(gè) ReadFile 函數(shù) return 之前執(zhí)行 f.Close 方法。
defer 語(yǔ)句常被用于成對(duì)的操作,如文件的打開(kāi)和關(guān)閉,加鎖和釋放鎖,連接的建立和斷開(kāi)等。不管多么復(fù)雜的操作,都可以保證資源被正確地釋放。
四、使用建議
- 優(yōu)先使用 error 返回值:Go 推薦使用顯式的錯(cuò)誤返回值,而不是 panic。
- 錯(cuò)誤信息要清晰:錯(cuò)誤信息應(yīng)該包含足夠的上下文,便于調(diào)試。
- 使用錯(cuò)誤包裝:使用 fmt.Errorf 和 %w 包裝錯(cuò)誤,保留原始錯(cuò)誤信息。
- 謹(jǐn)慎使用 panic:panic 應(yīng)該用于不可恢復(fù)的錯(cuò)誤,如程序初始化失敗。
- recover 只在必要時(shí)使用:recover 主要用于庫(kù)或框架中,避免程序崩潰,一般業(yè)務(wù)代碼不建議濫用。
到此這篇關(guān)于Go語(yǔ)言錯(cuò)誤和異常實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言錯(cuò)誤和異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例
這篇文章主要為大家介紹了Go中的格式化字符串fmt.Sprintf()和fmt.Printf()使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
Go語(yǔ)言變量與類(lèi)型使用簡(jiǎn)明指南
Go語(yǔ)言是靜態(tài)編程語(yǔ)言,在Go語(yǔ)言中數(shù)據(jù)類(lèi)型用于聲明函數(shù)和變量,這篇文章主要介紹了Go語(yǔ)言變量與類(lèi)型使用的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09
Go方法簡(jiǎn)單性和高效性的充分體現(xiàn)詳解
本文深入探討了Go語(yǔ)言中方法的各個(gè)方面,包括基礎(chǔ)概念、定義與聲明、特性、實(shí)戰(zhàn)應(yīng)用以及性能考量,文章充滿(mǎn)技術(shù)深度,通過(guò)實(shí)例和代碼演示,力圖幫助讀者全面理解Go方法的設(shè)計(jì)哲學(xué)和最佳實(shí)踐2023-10-10
Go結(jié)合JavaScript實(shí)現(xiàn)抓取網(wǎng)頁(yè)中的圖像鏈接
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言如何結(jié)合JavaScript實(shí)現(xiàn)抓取網(wǎng)頁(yè)中的圖像鏈接,文中的示例代碼講解詳細(xì),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11
Go語(yǔ)言fmt庫(kù)詳解與應(yīng)用實(shí)例(格式化輸入輸出功能)
fmt庫(kù)是Go語(yǔ)言中一個(gè)強(qiáng)大而靈活的庫(kù),提供了豐富的格式化輸入輸出功能,通過(guò)本文的介紹和實(shí)例演示,相信你對(duì)fmt庫(kù)的使用有了更深的理解,感興趣的朋友一起看看吧2023-10-10

