Go語言的函數能夠一次返回多個結果。這就爲咱們溫和地報告錯誤提供了語言級別的支持。實際上,這也是Go語言中處理錯誤的慣用法之一。咱們先來回顧前一小節的例子:函數
func readFile(path string) ([]byte, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() return ioutil.ReadAll(file) }
函數readFile
有兩個結果聲明。第二個結果聲明的類型是error
。error
是Go語言內置的一個接口類型。它的聲明是這樣的:編碼
type error interface { Error() string }
顯然,只要一個類型的方法集合包含了名爲Error
、無參數聲明且僅聲明瞭一個string
類型的結果的方法,就至關於實現了error
接口。os.Open
函數的第二個結果值就的類型就是這樣的。咱們把它賦給了變量err
。也許你已經意識到,在Go語言中,函數與其調用方之間溫和地傳遞錯誤的方法便是如此。
在調用了os.Open
函數並取得其結果以後,咱們判斷err
是否爲nil
。若是答案是確定的,那麼就直接把該錯誤(這裏由err
表明)返回給調用方。這條if
語句其實是一條衛述語句。這樣的語句會檢查流程中的某個步驟是否存在異常,並在必要時停止流程並報告給上層的程序(這裏是調用方)。在Go語言的標準庫以及不少第三方庫中,咱們常常能夠看到這樣的代碼。咱們也建議你們在本身的程序中善用這樣的衛述語句。
如今咱們把目光聚焦到readFile
函數中的最後一條語句上。這是一條return
語句。它把對ioutil.ReadAll
函數的調用的結果直接做爲readFile
函數的結果返回了。實際上,ioutil.ReadAll
函數的結果聲明列表與readFile
的結果聲明列表是一致的。也就是說,它們聲明的結果的數量、類型和順序都是相同的。所以,咱們纔可以作這種返回結果上的「嫁接」。這又是一個Go語言編碼中的慣用法。
好了,在知曉怎樣在傳遞錯誤以後,讓咱們來看看怎樣創造錯誤。沒錯,在不少時候,咱們須要創造出錯誤(即error
類型的值)並把它傳遞給上層程序。這很簡單。只需調用標準庫代碼包errors
的New
函數便可。例如,咱們只要在readFile
函數的開始處加入下面這段代碼就能夠更快的在參數值無效時告知調用方:spa
if path == "" { return nil, errors.New("The parameter is invalid!") }
errors.New
是一個很經常使用的函數。在Go語言標準庫的代碼包中有不少由此函數建立出來的錯誤值,好比os.ErrPermission
、io.EOF
等變量的值。咱們能夠很方便地用操做符==
來判斷一個error
類型的值與這些變量的值是否相等,從而來肯定錯誤的具體類別。就拿io.EOF
來講,它表明了一個信號。該信號用於通知數據讀取方已無更多數據可讀。咱們在獲得這樣一個錯誤的時候不該該把它當作一個真正的錯誤,而應該只去結束相應的讀取操做。請看下面的示例:code
br := bufio.NewReader(file) var buf bytes.Buffer for { ba, isPrefix, err := br.ReadLine() if err != nil { if err == io.EOF { break } fmt.Printf("Error: %s\n", err) break } buf.Write(ba) if !isPrefix { buf.WriteByte('\n') } }
能夠看到,這段代碼使用到了前面示例中的變量file
。它的功能是把file
表明的文件中的全部內容都讀取到一個緩衝器(由變量buf
表明)中。請注意,該示例中的第6~8行代碼。若是斷定err
表明的錯誤值等於io.EOF
的值(即它們是同一個值),那麼咱們只需退出當前的循環以使讀取操做結束便可。
總之,只要可以善用error
接口、errors.New
函數和比較操做符==
,咱們就能夠玩兒轉Go語言中的通常錯誤處理。接口