1. 程式人生 > >golang教程之自定義錯誤

golang教程之自定義錯誤

文章目錄

自定義錯誤

原文:https://golangbot.com/custom-errors/

在上一個教程中,我們學習瞭如何在Go中表示錯誤以及如何處理標準庫中的錯誤。 我們還學習瞭如何從標準庫錯誤中提取更多資訊。

本教程介紹如何建立我們自己的自定義錯誤,我們可以在我們建立的函式和包中使用它們。 我們還將使用標準庫使用的相同技術來提供有關我們的自定義錯誤的更多詳細資訊。

使用New函式建立自定義錯誤

建立自定義錯誤的最簡單方法是使用錯誤包的New函式。

在我們使用New函式建立自定義錯誤之前,讓我們瞭解它是如何實現的。 下面提供了錯誤包中New函式的實現。

// 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 }

實現非常簡單。 errorString是一個帶有單個字串欄位s的結構型別。error介面的Error() string方法是使用第14行中的errorString指標接收器實現的。

第5行的New函式,接受一個字串引數,使用該引數建立一個型別為errorString的值,並返回它的地址。 因此,建立並返回新錯誤。

現在我們知道了New函式的工作原理,讓我們在自己的程式中使用它來建立自定義錯誤。

我們將建立一個計算圓的面積的簡單程式,如果半徑為負,則返回錯誤。

package main

import (  
    "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.0
    area, err := circleArea(radius)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("Area of circle %0.2f", area)
}

在上面的程式中,我們檢查在第行10中半徑是否小於零。如果是,我們將返回零以及相應的錯誤訊息。 如果半徑大於0,則計算面積並返回nil作為第13行中的錯誤。

main函式中,我們檢查錯誤是否為nil,我們列印錯誤並返回,否則列印圓面積。

在這個程式中,半徑小於零,因此它將列印,

Area calculation failed, radius is less than zero 

使用Errorf向錯誤新增更多資訊

上面的程式效果很好,但是如果我們列印導致錯誤的實際半徑就不會很好。 這是fmt軟體包的Errorf函式派上用場的地方。此函式根據格式說明符格式化錯誤,並返回一個字串作為滿足錯誤的值。

讓我們使用Errorf函式,使程式更好。

package main

import (  
    "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.0
    area, err := circleArea(radius)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("Area of circle %0.2f", area)
}

在上面的程式中,Errorf用於列印導致錯誤的實際半徑。 執行此程式將輸出,

Area calculation failed, radius -20.00 is less than zero 

使用結構型別和欄位提供有關錯誤的更多資訊

也可以使用實現錯誤介面的結構型別作為錯誤。這為我們提供了更多的錯誤處理靈活性。在我們的示例中,如果我們想要訪問導致錯誤的半徑,現在唯一的方法是解析錯誤描述Area calculation failed, radius -20.00 is less than zero.。這不是一種正確的方法,因為如果描述發生變化,我們的程式碼就會中斷。

我們將使用前面教程中的“斷言底層結構型別並從結構域中獲取更多資訊”部分中解釋的標準庫所遵循的策略,並使用struct欄位來提供對導致錯誤的半徑的訪問。我們將建立一個實現錯誤介面的結構型別,並使用其欄位提供有關錯誤的更多資訊。

第一步是建立一個結構型別來表示錯誤。錯誤型別的命名約定是名稱應以文字Error結尾。所以我們將結構型別命名為areaError

type areaError struct {  
    err    string
    radius float64
}

上面的結構型別有一個欄位radius,它儲存負責該錯誤的半徑值,而err欄位儲存實際的錯誤訊息。

下一步是實現error介面。

func (e *areaError) Error() string {  
    return fmt.Sprintf("radius %0.2f: %s", e.radius, e.err)
}

在上面的程式碼片段中,我們使用指標接收器* areaError實現錯誤介面的Error() string方法。 此方法列印半徑和錯誤描述。

讓我們通過編寫main函式和circleArea函式來完成程式。

package main

import (  
    "fmt"
    "math"
)

type areaError struct {  
    err    string
    radius 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.0
    area, 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)
}

在上面的程式中,circleArea用於計算圓的面積。此函式首先檢查半徑是否小於零,如果是,則使用負責錯誤的半徑和相應的錯誤訊息建立型別areaError的值,然後在行第19行中返回它的地址。因此,我們提供了有關錯誤的更多資訊,在這種情況下,使用自定義錯誤結構的欄位描述錯誤的半徑。

在主函式第26行中,我們試圖找到半徑為-20的圓的面積。由於半徑小於零,將返回錯誤。

我們檢查錯誤是否存在,並在下一行中斷言它的型別* areaError。如果錯誤的型別為* areaError,則會得到導致第29行使用err.radius為0的半徑,列印自定義錯誤訊息並從程式返回。

如果斷言失敗,我們只需在第32行中列印錯誤。如果沒有錯誤,該面積將在第35行列印。

該程式將列印,

Radius -20.00 is less than zero  

現在讓我們使用上一個教程中描述的第二個策略,並使用自定義錯誤型別的方法來提供有關錯誤的更多資訊。

使用結構型別的方法提供有關錯誤的更多資訊

在本節中,我們將編寫一個計算矩形面積的程式。 如果長度或寬度小於零,該程式將列印錯誤。

第一步是建立一個表示錯誤的結構。

type areaError struct {  
    err    string //error description
    length float64 //length which caused the error
    width  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
}

在上面的程式碼片段中,我們從 Error() string方法返回錯誤的描述。 當length小於零時,lengthNegative() bool方法返回true,而當width小於零時,widthNegative() bool方法返回true。 這兩種方法提供了有關錯誤的更多資訊,在這種情況下,它們表示區域計算是否因為長度為負或寬度為負而失敗。 因此,我們使用struct error型別的方法來提供有關錯誤的更多資訊。

下一步是編寫面積計算功能。

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
}

上面的rectArea函式檢查長度或寬度是否小於零,如果是,則返回錯誤訊息,否則返回矩形面積,nil為錯誤。

讓我們通過建立main函式來完成這個程式。

func main() {  
    length, width := -5.0, -9.0
    area, 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,我們斷言它在下一行輸入* areaError。 然後使用lengthNegative()widthNegative()方法,檢查錯誤是否是因為長度為負或寬度為負。 我們列印相應的錯誤訊息並從程式返回。 因此,我們使用錯誤結構型別的方法來提供有關錯誤的更多資訊。

如果沒有錯誤,將列印矩形面積。

這是完整的程式供您參考。

package main

import "fmt"

type areaError struct {  
    err    string  //error description
    length float64 //length which caused the error
    width  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.0
    area, 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)
}

該程式將列印輸出,

error: length -5.00 is less than zero  
error: width -9.00 is less than zero  

我們已經看到了錯誤處理教程中描述的三種方法中的兩種的示例,以提供有關錯誤的更多資訊。

使用直接比較的第三種方式非常簡單。 我會留下它作為練習,讓您弄清楚如何使用此策略提供有關我們的自定義錯誤的更多資訊。

這使我們結束本教程。

以下是我們在本教程中學到的內容的快速回顧,

  • 使用New函式建立自定義錯誤

  • 使用Errorf向錯誤新增更多資訊

  • 使用結構型別和欄位提供有關錯誤的更多資訊

  • 使用結構型別的方法提供有關錯誤的更多資訊