Go的自定义错误

在上一篇教程中,我们了解了 Go 中的错误表示以及如何处理标准库中的错误。我们还学习了如何从错误中提取更多信息。

本教程介绍如何创建我们自己的自定义错误,我们可以在函数和包中使用这些错误。我们还将使用标准库所采用的相同技术来提供有关自定义错误的更多详细信息。

使用 New 函数创建自定义错误

创建自定义错误的最简单方法是使用错误包的New函数。

在我们使用 New函数创建自定义错误之前,我们先了解一下它是如何实现的。下面提供了错误包中 New 函数的实现。

package errors

// New returns an error that formats as the given text.
// Each call to New returns a distinct error value even if the text is identical.
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是具有单个字符串字段的结构类型。该接口的Error() string 方法error是使用第 1 4行中的errorString 指针接收器实现的。

第 5行中的New函数。 接受一个string参数,使用该参数创建一个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)
}

Run in playground

在上面的程序中,我们检查半径是否小于零。如果是这样,我们将返回nil以及相应的错误消息。如果半径大于 0,则计算面积

在 main 函数中,我们检查错误是否不等nil。 如果不是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)
}

Run in playground

在上面的程序中,运行该程序将输出,

Area calculation failed, radius -20.00 is less than zero  

使用结构类型和字段提供有关错误的更多信息

还可以使用将错误接口实现为错误的结构类型。这为我们提供了错误处理的更大灵活性。在我们前面的示例中,如果我们想要访问导致错误的半径,现在唯一的方法就是解析错误描述Area calculation failed, radius -20.00 is less than zero。这不是执行此操作的正确方法,因为如果描述发生更改,我们的代码就会中断。

我们将使用上一个教程中“将错误转换为基础类型并从结构体字段检索更多信息”部分中解释的标准库所遵循的策略,并使用结构体字段提供对导致错误的半径的访问。我们将创建一个实现错误接口的结构类型,并使用其字段来提供有关错误的更多信息。

第一步是创建一个结构类型来表示错误。错误类型的命名约定是名称应以文本结尾Error。所以让我们将结构类型命名为areaError

type areaError struct {  
    err    string
    radius float64
}

上面的结构类型有一个字段radius存储导致错误的半径值,该err字段存储实际的错误消息。

下一步是实现错误接口。

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

在上面的代码片段中,我们Error() string使用指针接收器实现了错误接口的方法。此方法打印半径和错误描述。

main我们通过编写函数和函数来完成程序circleArea

package main

import (  
    "errors"
    "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{
            err:    "radius is negative",
            radius: radius,
        }
    }
    return math.Pi * radius * radius, nil
}

func main() {  
    radius := -20.0
    area, err := circleArea(radius)
    if err != nil {
        var areaError *areaError
        if errors.As(err, &areaError) {
            fmt.Printf("Area calculation failed, radius %0.2f is less than zero", areaError.radius)
            return
        }
        fmt.Println(err)
        return
    }
    fmt.Printf("Area of rectangle %0.2f", area)
}

Run in playground

在上面的程序中,circleArea用于计算圆的面积。该函数首先检查半径是否小于零,如果是则使用导致错误的半径和相应的错误消息创建一个areaError类型值,然后返回它的地址以及0因此,我们提供了有关错误的更多信息,在本例中是使用自定义错误结构的字段导致错误的半径。

如果半径不为负,该函数将计算并返回面积以及nil

我们试图求出半径为 -20 的圆的面积。由于半径小于零,因此将返回错误。

我们检查错误是否不等nil我们尝试将其转换为*areaError 类型 。如果错误类型为\*areaError,我们将在第 1 行中获取导致错误的半径。使用areaError.radius,打印自定义错误消息并从程序返回。

如果错误不是类型*areaError,我们只需在第 1 行打印错误并返回。如果没有错误,该将打印在数值

该程序将打印

Area calculation failed, 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() stringlengthNegative() bool当长度小于零时,该方法返回 true;widthNegative() bool当宽度小于零时,该方法返回 true。这两种方法提供了有关错误的更多信息,在这种情况下,它们表示面积计算是否由于长度为负或宽度为负而失败。因此,我们使用了结构错误类型的方法来提供有关错误的更多信息。

下一步是编写面积计算函数。

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:    err,
            length: length,
            width:  width,
        }
    }
    return length * width, nil
}

上面的rectArea函数检查长度或宽度是否小于零,如果是,则返回类型为 *areaError的错误,否则返回带有nil错误的矩形面积。

让我们通过创建 main 函数来完成这个程序。

func main() {  
    length, width := -5.0, -9.0
    area, err := rectArea(length, width)
    if err != nil {
        var areaError *areaError
        if errors.As(err, &areaError) {
            if areaError.lengthNegative() {
                fmt.Printf("error: length %0.2f is less than zero\n", areaError.length)

            }
            if areaError.widthNegative() {
                fmt.Printf("error: width %0.2f is less than zero\n", areaError.width)

            }
            return
        }
        fmt.Println(err)
        return
    }
    fmt.Println("area of rect", area)
}

在 main 函数中,我们检查错误是否不等nil,如果它不为nil,我们尝试将其转换为*areaError类型。然后使用lengthNegative()widthNegative()方法检查错误是否是由于长度为负或宽度为负而导致的。我们打印相应的错误消息并从程序返回。因此,我们使用错误结构类型上的方法来提供有关错误的更多信息。

如果没有错误,将打印矩形的面积。

这是完整的程序供您参考。

package main

import (  
    "errors"
    "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:    err,
            length: length,
            width:  width,
        }
    }
    return length * width, nil
}

func main() {  
    length, width := -5.0, -9.0
    area, err := rectArea(length, width)
    if err != nil {
        var areaError *areaError
        if errors.As(err, &areaError) {
            if areaError.lengthNegative() {
                fmt.Printf("error: length %0.2f is less than zero\n", areaError.length)

            }
            if areaError.widthNegative() {
                fmt.Printf("error: width %0.2f is less than zero\n", areaError.width)

            }
            return
        }
        fmt.Println(err)
        return
    }
    fmt.Println("area of rect", area)
}

Run in playground

该程序将打印输出,

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

我们已经看到了错误处理教程中描述的三种方法中的两种的示例,以提供有关错误的更多信息。

使用直接比较的第三种方法非常简单。我将把它作为一个练习,让您了解如何使用此策略来提供有关我们的自定义错误的更多信息。

祝你有美好的一天。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值