Golang 全面深入系列之 Error

What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.

你所浪費的今天是昨天死去的人奢望的明天; 你所厭惡的現在是未來的你回不去的曾經。

Go errors 簡介

Go 的慣用法中,返回值不是整型等常用返回值類型,而是用了一個 error( interface 類型)。

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
 Error() string
}

就像任何其他內置類型,如 int,float64 一樣......錯誤值可以存儲在變量中,從函數返回等等。

package main

import (  
   "fmt"
   "os"
)

func main() {  
   f, err := os.Open("/test.txt")
   if err != nil {
       fmt.Println(err)
       return
   }
   fmt.Println(f.Name(), "opened successfully")
}

如果文件 test.txt 不存在, Open 方法會返回 error.

在 go 中處理錯誤的慣用方式是將返回的錯誤與 nil 進行比較。零值表示沒有發生錯誤,而非零值表示存在錯誤。在上面的例子中,我們檢查錯誤是否不爲 nil。如果不是,我們只需打印錯誤並從主函數返回。

錯誤類型定義

type error interface {  
   Error() string
}

我們知道,golang 中 error 的定義是一個接口類型, 任何實現此接口的類型都可以用作錯誤的輸出調用。此方法提供錯誤的描述。當打印錯誤時,fmt.println 函數在內部調用 Error()字符串方法來獲取錯誤的描述。

從錯誤中提取更多信息的不同方式

現在我們知道 error 是一種接口類型,可以讓我們看看如何提取更多有關的錯誤信息。例如:上面例子中,我們想提取導致錯誤的文件路徑該如何獲取?

1.聲明底層結構類型並從結構字段獲取更多信息

如果仔細閱讀 open 函數的文檔,可以看到它返回了類型 *Patherror 的錯誤。Patherror 是一個結構類型

Open方法的實現:

func Open(name string) (*File, error) {
 return OpenFile(name, O_RDONLY, 0)
}

OpenFile方法的實現:

// OpenFile is the generalized open call; most users will use Open
// or Create instead. It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful,
// methods on the returned File can be used for I/O.
// If there is an error, it will be of type *PathError.
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
 if name == "" {
   return nil, &PathError{"open", name, syscall.ENOENT}
 }
 r, errf := openFile(name, flag, perm)
 if errf == nil {
   return r, nil
 }
 r, errd := openDir(name)
 if errd == nil {
   if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
     r.Close()
     return nil, &PathError{"open", name, syscall.EISDIR}
   }
   return r, nil
 }
 return nil, &PathError{"open", name, errf}
}

其中發生錯誤都返回了*PathError這個結構體實例。那麼我們來看看這個結構體的具體信息。

// PathError records an error and the operation and file path that caused it.
type PathError struct {
 Op   string
 Path string
 Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

此結構體實現了error接口。 結構體元素中也包括了 Path 的相關信息。

那麼我們修改一上示例的代碼:

package main

import (
 "fmt"
 "os"
)

func main() {
 f, err := os.Open("/test.txt")
 if errObject, ok := err.(*os.PathError); ok {
   fmt.Println("錯誤輸出:",err, "文件路徑:", errObject.Path)
   return
 }
 fmt.Println(f.Name(), "opened successfully")
}

此時的 errObject 就是 PathError 的一個實例。

現在,我們已成功使用類型斷言來從錯誤中獲取文件路徑。

2.聲明底層結構類型並使用方法獲取更多信息

通過調用結構類型的方法來斷言底層類型並獲取更多信息。

讓我們通過一個例子來更好地理解這一點。標準庫中的DNSError結構類型定義如下:

type DNSError struct {  
   ...
}

func (e *DNSError) Error() string {  
   ...
}
func (e *DNSError) Timeout() bool {  
   ... 
}
func (e *DNSError) Temporary() bool {  
   ... 
}

從上面的代碼可以看出,DNSError 結構有兩個方法 timeout()bool 和 temporary()bool,它們返回一個布爾值,表示錯誤是由於超時還是臨時性的。

package main

import (
 "fmt"
 "net"
)

func main() {
 addr, err := net.LookupHost("wwwwwwwwww.xxxx")
 if err, ok := err.(*net.DNSError); ok {
   if err.Timeout() {
     fmt.Println("operation timed out")
   } else if err.Temporary() {
     fmt.Println("temporary error")
   } else {
     fmt.Println("generic error: ", err)
   }
   return
 }
 fmt.Println(addr)
}

程序會輸出:generic error: lookup wwwwwwwwww.xxxx: no such host, 我們就可以確定錯誤既不是暫時性的,也不是由於超時。

3.直接比較

package main

import (  
   "fmt"
   "path/filepath"
)

func main() {  
   files, error := filepath.Glob("[")
   if error != nil && error == filepath.ErrBadPattern {
       fmt.Println(error)
       return
   }
   fmt.Println("matched files", files)
}

如果 Glob() 的模式錯誤,就會報 ErrBadPattern 的錯誤類型。

最後一點:

永遠不要忽略錯誤

記住!!!多寫一點小麻煩,省掉千千萬萬個×××煩!!!

最後,寫的有點匆忙,有錯誤之處還望指正!

本文來自:開源中國博客

感謝作者:90design

查看原文:Golang全面深入系列之 ErrorError

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章