這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原創文章,轉載請註明出處:伺服器非業餘研究-sunface
對於Go來說錯誤如何處理是非常關鍵和重要的,這裡我總結了一些方法可以避免錯誤的錯誤處理機制(具體見關於go error處理風格的一些討論和個人觀點(上))
1.錯誤碼常用風格:
以下兩個例子第一個例子明顯好過第二個
f, err := os.Open(path)
if err != nil {
// handle error
}
// do stuff
f, err := os.Open(path)
if err == nil {
// do stuff
}
// handle error
2.自訂錯誤處理風格:
首先定義錯誤介面:
type Error string
func (e Error) Error() string { return string(e) }
然後用類型斷言判斷錯誤類型
result, err := yourpackage.Foo()
if ype, ok := err.(yourpackage.Error); ok {
// use ype to handle error
}
分析檔案,並輸出錯誤資訊:
type OpenError struct {
File *File
Error string
}
func (oe *OpenError) Error() string {
// format error string here
}
func ParseFiles(files []*File) error {
for _, f := range files {
err := f.parse()
if err != nil {
return &OpenError{
File: f,
Error: err.Error(),
}
}
}
}
這樣就知道具體哪個檔案在分析的時候失敗了。
有一點要特別注意:封裝錯誤,因為當你封裝錯誤的時候,原本的錯誤資訊可能會丟失:
var c net.Conn
f, err := DownloadFile(c, path)
switch e := err.(type) {
case net.Error:
// 關閉串連
c.Close()
return e
case error:
// error != nil
return err
default:
// err == nil的時候會執行這裡
}
// do other things.
如果在這段代碼中你封裝了net.Error,則這段代碼不會再認為是net方面出錯了,也就不會關閉串連,繼續使用之前錯誤的串連。一個比較好的辦法是:當使用外部介面的時候,不要封裝它們產生的錯誤!!
3.錯誤就像狀態一樣:
有的時候你會暫時不處理一個錯誤,可能因為想延後處理,也可能因為錯誤會很快再出現一次。
對於第一種情況,bufio就是一個很好的例子,當bufio.Reader遇到一個錯誤的時候,它會持有那個錯誤直到緩衝為空白,才會處理這個錯誤。
對於第二種情況,go/loader是一個很好的例子,當它被調用且發生錯誤的時候,會持有那個錯誤,因為很可能它會被用相當的參數再調用一次。
4.使用函數來避免重複
如果某種錯誤在不斷重複的出現,就可以使用函數來簡化:
func handleError(c net.Conn, err error) {
// 進行錯誤後續處理
}
func DoStuff(c net.Conn) error {
f, err := downloadFile(c, path)
if err != nil {
handleError(c, err)
return err
}
f, err := doOtherThing(c)
if err != nil {
handleError(c, err)
return err
}
}
或者可以使用下面這種風格:
func handleError(c net.Conn, err error) {
if err == nil {
return
}
//錯誤後續處理
}
func DoStuff(c net.Conn) error {
defer func() { handleError(c, err) }()
f, err := downloadFile(c, path)
if err != nil {
return err
}
f, err := doOtherThing(c)
if err != nil {
return err
}
}