Go 系列教程 —— 31. 自訂錯誤

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。![custom errors](https://raw.githubusercontent.com/studygolang/gctt-images/master/golang-series/custom-errors-golang-1.png)歡迎來到 [Golang 系列教程](https://studygolang.com/subject/2)的第 31 篇。在[上一教程](https://studygolang.com/articles/12724)裡,我們學習了 Go 中的錯誤是如何表示的,並學習了如何處理標準庫裡的錯誤。我們還學習了從標準庫的錯誤中提取更多的資訊。在本教程中,我們會學習如何建立我們自己的自訂錯誤,並在我們建立的函數和包中使用它。我們會使用與標準庫中相同的技術,來提供自訂錯誤的更多細節資訊。## 使用 New 函數建立自訂錯誤建立自訂錯誤最簡單的方法是使用 [`errors`](https://golang.org/pkg/errors/) 包中的 [`New`](https://golang.org/pkg/errors/#New) 函數。在使用 New [函數](https://studygolang.com/articles/11892) 建立自訂錯誤之前,我們先來看看 `New` 是如何?的。如下所示,是 [`errors` 包](https://golang.org/src/errors/errors.go?s=293:320#L1) 中的 `New` 函數的實現。```go// Package errors implements functions to manipulate errors.package errors// New returns an error that formats as the given text.func New(text string) error {return &errorString{text}}// errorString is a trivial implementation of error.type errorString struct {s string}func (e *errorString) Error() string {return e.s}````New` 函數的實現很簡單。`errorString` 是一個[結構體](https://studygolang.com/articles/12263)類型,只有一個字串欄位 `s`。第 14 行使用了 `errorString` 指標接受者(Pointer Receiver),來實現 `error` 介面的 `Error() string` [方法](https://studygolang.com/articles/12264)。第 5 行的 `New` 函數有一個字串參數,通過這個參數建立了 `errorString` 類型的變數,並返回了它的地址。於是它就建立並返回了一個新的錯誤。現在我們已經知道了 `New` 函數是如何工作的,我們開始在程式裡使用 `New` 來建立自訂錯誤吧。我們將建立一個計算圓半徑的簡單程式,如果半徑為負,它會返回一個錯誤。```gopackage mainimport ( "errors""fmt""math")func circleArea(radius float64) (float64, error) { if radius < 0 {return 0, errors.New("Area calculation failed, radius is less than zero")}return math.Pi * radius * radius, nil}func main() { radius := -20.0area, err := circleArea(radius)if err != nil {fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area)}```[在 glayground 上運行](https://play.golang.org/p/_vuf6fgkqm)在上面的程式中,我們檢查半徑是否小於零(第 10 行)。如果半徑小於零,我們會返回等於 0 的面積,以及相應的錯誤資訊。如果半徑大於零,則會計算出面積,並傳回值為 `nil` 的錯誤(第 13 行)。在 `main` 函數裡,我們在第 19 行檢查錯誤是否等於 `nil`。如果不是 `nil`,我們會列印出錯誤並返回,否則我們會列印出圓的面積。在我們的程式中,半徑小於零,因此列印出:```Area calculation failed, radius is less than zero ```## 使用 Errorf 給錯誤添加更多資訊上面的程式效果不錯,但是如果我們能夠列印出當前圓的半徑,那就更好了。這就要用到 [`fmt`](https://golang.org/pkg/fmt/) 包中的 [`Errorf`](https://golang.org/pkg/fmt/#Errorf) 函數了。`Errorf` 函數會根據格式說明符,規定錯誤的格式,並返回一個符合該錯誤的[字串](https://studygolang.com/articles/12261)。接下來我們使用 `Errorf` 函數來改進我們的程式。```gopackage mainimport ( "fmt""math")func circleArea(radius float64) (float64, error) { if radius < 0 {return 0, fmt.Errorf("Area calculation failed, radius %0.2f is less than zero", radius)}return math.Pi * radius * radius, nil}func main() { radius := -20.0area, err := circleArea(radius)if err != nil {fmt.Println(err)return}fmt.Printf("Area of circle %0.2f", area)}```[在 playground 上運行](https://play.golang.org/p/HQ7bvjT4o2)在上面的程式中,我們使用 `Errorf`(第 10 行)列印了發生錯誤的半徑。程式運行後會輸出:```Area calculation failed, radius -20.00 is less than zero ```## 使用結構體類型和欄位提供錯誤的更多資訊錯誤還可以用實現了 `error` [介面](https://studygolang.com/articles/12266)的結構體來表示。這種方式可以更加靈活地處理錯誤。在上面例子中,如果我們希望訪問引發錯誤的半徑,現在唯一的方法就是解析錯誤的描述資訊 `Area calculation failed, radius -20.00 is less than zero`。這樣做不太好,因為一旦描述資訊發生變化,程式就會出錯。我們會使用標準庫裡採用的方法,在上一教程中“斷言底層結構體類型,使用結構體欄位擷取更多資訊”這一節,我們講解了這一方法,可以使用結構體欄位來訪問引發錯誤的半徑。我們會建立一個實現 `error` 介面的結構體類型,並使用它的欄位來提供關於錯誤的更多資訊。第一步就是建立一個表示錯誤的結構體類型。錯誤類型的命名規範是名稱以 `Error` 結尾。因此我們不妨把結構體類型命名為 `areaError`。```gotype areaError struct { err stringradius float64}```上面的結構體類型有一個 `radius` 欄位,它儲存了與錯誤有關的半徑,而 `err` 欄位儲存了實際的錯誤資訊。下一步是實現 `error` 介面。```gofunc (e *areaError) Error() string { return fmt.Sprintf("radius %0.2f: %s", e.radius, e.err)}```在上面的代碼中,我們使用指標接收者 `*areaError`,實現了 `error` 介面的 `Error() string` 方法。該方法列印出半徑和關於錯誤的描述。現在我們來編寫 `main` 函數和 `circleArea` 函數來完成整個程式。```gopackage mainimport ( "fmt""math")type areaError struct { err stringradius float64}func (e *areaError) Error() string { return fmt.Sprintf("radius %0.2f: %s", e.radius, e.err)}func circleArea(radius float64) (float64, error) { if radius < 0 {return 0, &areaError{"radius is negative", radius}}return math.Pi * radius * radius, nil}func main() { radius := -20.0area, err := circleArea(radius)if err != nil {if err, ok := err.(*areaError); ok {fmt.Printf("Radius %0.2f is less than zero", err.radius)return}fmt.Println(err)return}fmt.Printf("Area of rectangle1 %0.2f", area)}```[在 playground 上運行](https://play.golang.org/p/OTs7J0adQg)在上面的程式中,`circleArea`(第 17 行)用於計算圓的面積。該函數首先檢查半徑是否小於零,如果小於零,它會通過錯誤半徑和對應錯誤資訊,建立一個 `areaError` 類型的值,然後返回 `areaError` 值的地址,與此同時 `area` 等於 0(第 19 行)。**於是我們提供了更多的錯誤資訊(即導致錯誤的半徑),我們使用了自訂錯誤的結構體欄位來定義它**。如果半徑是非負數,該函數會在第 21 行計算並返回面積,同時錯誤值為 `nil`。在 `main` 函數的 26 行,我們試圖計算半徑為 -20 的圓的面積。由於半徑小於零,因此會導致一個錯誤。我們在第 27 行檢查了錯誤是否為 `nil`,並在下一行斷言了 `*areaError` 類型。**如果錯誤是 `*areaError` 類型,我們就可以用 `err.radius` 來擷取錯誤的半徑(第 29 行),列印出自訂錯誤的訊息,最後程式返回退出**。如果斷言錯誤,我們就在第 32 行列印該錯誤,並返回。如果沒有發生錯誤,在第 35 行會列印出面積。該程式會輸出:```Radius -20.00 is less than zero ```下面我們來使用上一教程提到的[第二種方法](https://studygolang.com/articles/12724),使用自訂錯誤類型的方法來提供錯誤的更多資訊。## 使用結構體類型的方法來提供錯誤的更多資訊在本節裡,我們會編寫一個計算矩形面積的程式。如果長或寬小於零,程式就會列印出錯誤。第一步就是建立一個表示錯誤的結構體。```gotype areaError struct { err string //error descriptionlength float64 //length which caused the errorwidth float64 //width which caused the error}```上面的結構體類型除了有一個錯誤描述欄位,還有可能引發錯誤的寬和高。現在我們有了錯誤類型,我們來實現 `error` 介面,並給該錯誤類型添加兩個方法,使它提供了更多的錯誤資訊。```gofunc (e *areaError) Error() string { return e.err}func (e *areaError) lengthNegative() bool { return e.length < 0}func (e *areaError) widthNegative() bool { return e.width < 0}```在上面的程式碼片段中,我們從 `Error() string` 方法中返回了關於錯誤的描述。當 `length` 小於零時,`lengthNegative() bool` 方法返回 `true`,而當 `width` 小於零時,`widthNegative() bool` 方法返回 `true`。**這兩個方法都提供了關於錯誤的更多資訊,在這裡,它提示我們計算面積失敗的原因(長度為負數或者寬度為負數)。於是我們就有了兩個錯誤類型結構體的方法,來提供更多的錯誤資訊**。下一步就是編寫計算面積的函數。```gofunc rectArea(length, width float64) (float64, error) { err := ""if length < 0 {err += "length is less than zero"}if width < 0 {if err == "" {err = "width is less than zero"} else {err += ", width is less than zero"}}if err != "" {return 0, &areaError{err, length, width}}return length * width, nil}```上面的 `rectArea` 函數檢查了長或寬是否小於零,如果小於零,`rectArea` 會返回一個錯誤資訊,否則 `rectArea` 會返回矩形的面積和一個值為 `nil` 的錯誤。讓我們建立 `main` 函數來完成整個程式。```gofunc main() { length, width := -5.0, -9.0area, err := rectArea(length, width)if err != nil {if err, ok := err.(*areaError); ok {if err.lengthNegative() {fmt.Printf("error: length %0.2f is less than zero\n", err.length)}if err.widthNegative() {fmt.Printf("error: width %0.2f is less than zero\n", err.width)}return}fmt.Println(err)return}fmt.Println("area of rect", area)}```在 `main` 程式中,我們檢查了錯誤是否為 `nil`(第 4 行)。如果錯誤值不是 `nil`,我們會在下一行斷言 `*areaError` 類型。然後,我們使用 `lengthNegative()` 和 `widthNegative()` 方法,檢查錯誤的原因是長度小於零還是寬度小於零。這樣我們就使用了錯誤結構體類型的方法,來提供更多的錯誤資訊。如果沒有錯誤發生,就會列印矩形的面積。下面是整個程式的代碼供你參考。```gopackage mainimport "fmt"type areaError struct { err string //error descriptionlength float64 //length which caused the errorwidth float64 //width which caused the error}func (e *areaError) Error() string { return e.err}func (e *areaError) lengthNegative() bool { return e.length < 0}func (e *areaError) widthNegative() bool { return e.width < 0}func rectArea(length, width float64) (float64, error) { err := ""if length < 0 {err += "length is less than zero"}if width < 0 {if err == "" {err = "width is less than zero"} else {err += ", width is less than zero"}}if err != "" {return 0, &areaError{err, length, width}}return length * width, nil}func main() { length, width := -5.0, -9.0area, err := rectArea(length, width)if err != nil {if err, ok := err.(*areaError); ok {if err.lengthNegative() {fmt.Printf("error: length %0.2f is less than zero\n", err.length)}if err.widthNegative() {fmt.Printf("error: width %0.2f is less than zero\n", err.width)}return}fmt.Println(err)return}fmt.Println("area of rect", area)}```[在 playground 上運行](https://play.golang.org/p/iJv2V8pZ7c)該程式會列印輸出:```error: length -5.00 is less than zero error: width -9.00 is less than zero ```在上一教程[錯誤處理](https://studygolang.com/articles/12724)中,我們介紹了三種提供更多錯誤資訊的方法,現在我們已經看了其中兩個樣本。第三種方法使用的是直接比較,比較簡單。我留給讀者作為練習,你們可以試著使用這種方法來給出自訂錯誤的更多資訊。本教程到此結束。簡單概括一下本教程討論的內容:- 使用 `New` 函數建立自訂錯誤- 使用 `Error` 添加更多錯誤資訊- 使用結構體類型和欄位,提供更多錯誤資訊- 使用結構體類型和方法,提供更多錯誤資訊祝你愉快。**上一教程 - [錯誤處理](https://studygolang.com/articles/12724)****下一教程 - [panic 和 recover](https://studygolang.com/articles/12785)**

via: https://golangbot.com/custom-errors/

作者:Nick Coghlan 譯者:Noluye 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

1607 次點擊  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.