Go语言 自定义错误

文章目录

    • 导言
    • 自定义错误
      • 使用 `New`函数 自定义错误
      • 使用 `Errorf`函数,输出格式化的错误
      • 通过结构体字段,为错误添加更多信息
      • 通过结构体方法,为错误添加更多信息
    • 原作者留言
    • 最后


导言

  • 原文链接: Part 31: Custom Errors
  • If translation is not allowed, please leave me in the comment area and I will delete it as soon as possible.

自定义错误

在本教程中,我们将讨论如何自定义错误。之后,我们将使用标准库提供的方法,去让错误拥有更多的信息。


使用 New函数 自定义错误

通过 errors包 的 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 是一个结构体,它具有一个 string类型 的 s字段。通过指针接收器,errorString结构 实现了 error接口。

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

  • 如果小于 0,我们返回 0面积、以及相应的错误。
  • 如果大于等于 0,我们返回 相应的面积、以及 nil错误。

main函数中,我们判断错误是否为 nil

  • 如果错误不为 nil,我们就输出相应的错误。
  • 如果错误为 nil,我们就输出算得的面积。

在上面的程序中,由于半径小于 0,所以程序输出为:

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

在上面程序的第 10 行,我们使用 Errorf函数,将半径格式化进错误内部。

运行程序,程序输出如下:

Area calculation failed, radius -20.00 is less than zero  

通过结构体字段,为错误添加更多信息

在上面的程序中,如果想访问 引发错误的半径,我们可以解析错误描述信息: Area calculation failed, radius -20.00 is less than zero。但是,这种方法不够优美,而且在错误描述信息改变时,我们的代码也将无法运行。

那如何实现这样的目的呢?我们可以将半径封装进结构体,并让该结构体实现 error接口。通过这样方式,我们能实现更加灵活的错误处理。

接下来,我们来创建一个代表错误的结构体。

type areaError struct {
       
    err    string
    radius float64
}

一般来说,错误类型命名的后缀一定要是 Error。因此,我们将该结构体命名为 areaError

在上面的结构体中,err字段 存储了 错误的描述信息,而 radius字段 存储了 引发错误的半径。

接下来,我们让该结构体实现 error接口。

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

添加了 maincircleArea函数 后,完整程序如下:

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函数 能计算圆的面积。这个函数会先判断半径:

  • 如果半径小于 0,它将 引发错误的半径、相应的错误描述信息 封装进 areaError结构后,返回 0面积、areaError结构的地址。
  • 如果半径大于等于 0,则直接返回 圆的面积、nil错误。

main函数 中,我们试着求取 半径为-20的圆 的面积。因为半径小于 0,所以 circleArea函数 将返回错误。

在第 27 行,我们对 circleArea函数返回的错误 进行判断:

  • 如果为 nil,我们就输出面积。
  • 如果不为 nil,我们将错误断言为 *areaError类型,
    • 如果断言成功,通过 err.radius,我们就能获取到错误内部的 radius
    • 如果断言失败,我们只是简单的输出错误。

程序输出如下:

Radius -20.00 is less than zero  

通过结构体方法,为错误添加更多信息

这一节,我们将写一个程序,这个程序功能为:计算矩形的面积,矩形长度或宽度为 0 时返回错误。

先创建一个代表错误的结构体:

type areaError struct {
       
    err    string //error description
    length float64 //length which caused the error
    width  float64 //width which caused the error
}

areaError结构体 包含了 错误的描述信息err、矩形长度length、矩形宽度width

接下来,我们让 areaError结构 实现 error接口,并为其添加 lengthNegativewidthNagative方法,这 2 个方法用以提供更多的错误信息。

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方法 会返回错误的描述信息。lengthNegativewidthNegative方法 会返回布尔值,2 个方法的布尔值分别表示:长度是否小于 0、宽度是否小于 0

之所以 lengthNegativewidthNegative方法 能提供更多的错误信息,是因为通过调用这 2 个方法,我们就能知道:错误到底是因长度小于 0 引起的,还是因宽度小于 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
}

rectArea函数 会先对长度、宽度进行判断:

  • 如果长度或宽度小于0,则返回 0面积、相应错误。
  • 如果长度和宽度都大于等于 0,则返回 矩形面积、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 函数中,我们对 rectArea函数返回的错误 进行判断:

  • 如果错误为 nil,此时表示没有错误,程序直接输出矩形面积。
  • 如果错误不为 nil,则将错误断言为 *areaError类型。
    • 如果断言成功,则调用 lengthNegativewidthNegative方法,判断错误的产生原因,并根据产生原因,输出相应的内容。
    • 如果断言失败,直接输出错误。

最终程序如下:

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("area of rect", area)
}

运行程序,输出如下:

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

在 Go语言 错误处理 这一节中,我们描述了 3从错误中获取更多信息 的方法,但在这一节,我只是写了 2为错误添加更多信息 的方法。

为错误添加更多信息的第 3 个方法是 直接比较。这个方法很简单,我将它留作练习。你们可以想想,怎么使用这个方法,为错误添加更多信息。

这就是全部内容了~

祝你长高高~


原作者留言

优质内容来之不易,您可以通过该 链接 为我捐赠。


最后

感谢原作者的优质内容。

欢迎指出文中的任何错误。

你可能感兴趣的:(go语言,翻译,go,golang,错误,自定义错误)