Go開發(fā)工程師
未來3-5年企業(yè)高性能項目不可替代的語言,從基礎(chǔ)到項目實戰(zhàn)再到重構(gòu),真正從入門到精通
Go語言的函數(shù)可以一次返回多個結(jié)果。這就為我們溫和地報告錯誤提供了語言級別的支持。實際上,這也是Go語言中處理錯誤的慣用法之一。我們先來回顧前一小節(jié)的例子:
func readFile(path string) ([]byte, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return ioutil.ReadAll(file)
}
函數(shù)readFile有兩個結(jié)果聲明。第二個結(jié)果聲明的類型是error。error是Go語言內(nèi)置的一個接口類型。它的聲明是這樣的:
type error interface {
Error() string
}
顯然,只要一個類型的方法集合包含了名為Error、無參數(shù)聲明且僅聲明了一個string類型的結(jié)果的方法,就相當(dāng)于實現(xiàn)了error接口。os.Open函數(shù)的第二個結(jié)果值就的類型就是這樣的。我們把它賦給了變量err。也許你已經(jīng)意識到,在Go語言中,函數(shù)與其調(diào)用方之間溫和地傳遞錯誤的方法即是如此。
在調(diào)用了os.Open函數(shù)并取得其結(jié)果之后,我們判斷err是否為nil。如果答案是肯定的,那么就直接把該錯誤(這里由err代表)返回給調(diào)用方。這條if語句實際上是一條衛(wèi)述語句。這樣的語句會檢查流程中的某個步驟是否存在異常,并在必要時中止流程并報告給上層的程序(這里是調(diào)用方)。在Go語言的標(biāo)準(zhǔn)庫以及很多第三方庫中,我們經(jīng)??梢钥吹竭@樣的代碼。我們也建議大家在自己的程序中善用這樣的衛(wèi)述語句。
現(xiàn)在我們把目光聚焦到readFile函數(shù)中的最后一條語句上。這是一條return語句。它把對ioutil.ReadAll函數(shù)的調(diào)用的結(jié)果直接作為readFile函數(shù)的結(jié)果返回了。實際上,ioutil.ReadAll函數(shù)的結(jié)果聲明列表與readFile的結(jié)果聲明列表是一致的。也就是說,它們聲明的結(jié)果的數(shù)量、類型和順序都是相同的。因此,我們才能夠做這種返回結(jié)果上的“嫁接”。這又是一個Go語言編碼中的慣用法。
好了,在知曉怎樣在傳遞錯誤之后,讓我們來看看怎樣創(chuàng)造錯誤。沒錯,在很多時候,我們需要創(chuàng)造出錯誤(即error類型的值)并把它傳遞給上層程序。這很簡單。只需調(diào)用標(biāo)準(zhǔn)庫代碼包errors的New函數(shù)即可。例如,我們只要在readFile函數(shù)的開始處加入下面這段代碼就可以更快的在參數(shù)值無效時告知調(diào)用方:
if path == "" {
return nil, errors.New("The parameter is invalid!")
}
errors.New是一個很常用的函數(shù)。在Go語言標(biāo)準(zhǔn)庫的代碼包中有很多由此函數(shù)創(chuàng)建出來的錯誤值,比如os.ErrPermission、io.EOF等變量的值。我們可以很方便地用操作符==來判斷一個error類型的值與這些變量的值是否相等,從而來確定錯誤的具體類別。就拿io.EOF來說,它代表了一個信號。該信號用于通知數(shù)據(jù)讀取方已無更多數(shù)據(jù)可讀。我們在得到這樣一個錯誤的時候不應(yīng)該把它看成一個真正的錯誤,而應(yīng)該只去結(jié)束相應(yīng)的讀取操作。請看下面的示例:
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代表的文件中的所有內(nèi)容都讀取到一個緩沖器(由變量buf代表)中。請注意,該示例中的第6~8行代碼。如果判定err代表的錯誤值等于io.EOF的值(即它們是同一個值),那么我們只需退出當(dāng)前的循環(huán)以使讀取操作結(jié)束即可。
總之,只要能夠善用error接口、errors.New函數(shù)和比較操作符==,我們就可以玩兒轉(zhuǎn)Go語言中的一般錯誤處理。
命令源碼文件index.go的功能是讀取自己的內(nèi)容。這其中用到了readFile函數(shù)。請注意,我把這個函數(shù)中的最后一條語句替換成了return read(file)。你現(xiàn)在的任務(wù)是,結(jié)合本節(jié)“知識要點”中的最后一段代碼編寫出一個完整的read函數(shù),使得該文件可以正確地被運行,并在標(biāo)準(zhǔn)輸出打印出index.go文件的內(nèi)容。
該文件中的導(dǎo)入語句中已列出了所有可能會用到的標(biāo)準(zhǔn)庫代碼包。在Go語言官方的文檔網(wǎng)站上可以查到其中各種程序?qū)嶓w的用法。
注意:由于本編程課件中無法模擬真實的程序運行環(huán)境,所以它讀取index.go文件的操作是不會成功的。請你把index.go中的代碼(包括你寫的read函數(shù))復(fù)制到一臺Linux或Mac機器上再加以運行。
read函數(shù)的實現(xiàn)可以是:
func read(r io.Reader) ([]byte, error) {
br := bufio.NewReader(r)
var buf bytes.Buffer
for {
ba, isPrefix, err := br.ReadLine()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
buf.Write(ba)
if !isPrefix {
buf.WriteByte('\n')
}
}
return buf.Bytes(), nil
}
請驗證,完成請求
由于請求次數(shù)過多,請先驗證,完成再次請求
打開微信掃碼自動綁定
綁定后可得到
使用 Ctrl+D 可將課程添加到書簽
舉報