自定义错误类型

上一节我们讨论了Golan中如何表示错误,如何处理Golang标准库中的错误,以及如何获取更多的错误细节信息。

本节主要讨论下如何自定义错误类型,我们仍然基于上一节介绍的几种方式来提供错误细节信息。

通过errors.New()函数来创建错误值

调用某个函数,比如filepath.Glob()会返回错误,这里说的错误其实就是一个错误值。在Golang中,我们可以直接通过errors包中的函数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定义了方法Error(),因此也就实现了接口error。注意这里定义方法Error()时使用的是指针接收器,因此我们在New方法中也返回了指针。

下面我们就使用New函数来创建一个错误值(注意其实是个指针)。

我们实现一个程序来计算圆形面积,如果半径小于0,需要返回一个错误。

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

函数circleArea中,我们判断入参radius是否小于0,如果是,则返回了一个通过errors.New()创建的错误值,否则返回正常的计算结果。注意这里如果入参是否合我们都同时返回两个值,第一个参数表示面积,第二个参数表示错误值。

该程序执行结果如下:

Area calculation failed, radius is less than zero

自定义结构体错误类型并通过其属性提供更多细节信息

Golang中所有错误类型均实现了error接口,因此我们也可以自定义结构体类型,只要实现error接口即可。我们可以将错误细节信息,比如前面例子中的radius存在某个属性上。下面我们就来实现这样一个自定义结构体错误类型。

首先我们创建一个结构体类型,错误类型一般以Error作为后缀,因此我们命名为areaError

type areaError struct {
    err string
    radius float64
}

该结构体类型包含一个radius属性,用来存放引起该错误的细节信息,即半径值。err属性用来存放错误文本信息。

接着我们通过实现Error() string方法来实现error接口。

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

注意我们使用的是指针接收器。下面我们实现main函数来使用下该自定义错误类型。

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

这里需要注意,由于我们定义的错误类型是按指针接收器实现的error接口,因此我们在circleArea函数中返回错误时需要返回areaError结构体变量的指针。

我们在main函数中去断言错误的类型,如果确实是*areaError,则说明是我们定义的错误类型,因此该结构体变量有一个radius属性来提供具体的不合法半径值。

自定义结构体错误类型并通过其方法提供更多细节信息

只要自定义结构体类型来实现error接口,我们不仅可以通过某些属性来提供具体错误细节信息,也可以通过方法来提供一些错误细节。比如下面这个例子,用来计算长方形的面积。

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

基于变量比较也可以来提供一些错误信息,比较简单,这里就不举例子了,感兴趣的可以自己实现下。

Last updated