@phper
2018-03-13T18:07:44.000000Z
字数 6128
阅读 2848
Golang
原文:https://golangbot.com/custom-errors/
欢迎访问Golang 系列教程中的第31章。
在上一教程中, 我们了解了如何在 go 中表示错误以及如何处理标准库中的错误。我们还学习了如何从标准库错误中提取更多的信息。
本教程讨论如何创建我们自己的自定义错误, 我们可以在我们创建的函数和包中使用它。我们还将使用标准库所采用的相同技术来提供有关我们的自定义错误的更多详细信息。
创建自定义错误的最简单方法是使用errors包的New函数。
在使用New函数创建自定义错误之前, 让我们了解它是如何实现的。下面提供了errors包中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
方法是使用errorString
指针接收器在第14行。
第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)
}
在上面的程序中, 我们在第10行检查半径是否小于0。如如果少于0, 我们返回零的面积连同相应的错误消息。如果半径大于0, 则计算该区域, 并在第13行中返回nil
错误。
在main
函数中, 在第19行我们检查错误是否为nil
。如果不是nil
, 则打印错误并返回, 否则将打印圆的面积。
在这个程序中半径小于零, 因此它将打印,
Area calculation failed, radius is less than zero
上述程序运行良好,但如果我们打印引起错误的实际半径,这不是很好。这就是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
在第10行中使用, 以打印导致错误的实际半径。运行此程序将输出,
Area calculation failed, radius -20.00 is less than zero
也可以使用实现error接口
的结构类型
作为错误。这为我们提供了更多的错误处理灵活性。在我们的例子中,如果我们想要访问导致错误的半径,现在唯一的办法是解析错误描述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)
}
在上面的代码段中, 我们使用指针接收器 * 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)
}
在操场上奔跑
在上面的程序中, 17 行中的circleArea用于计算圆的面积。此函数首先检查半径是否小于零, 如果这样, 它将使用负责错误的半径和相应的错误信息创建areaError类型的值, 然后以0的形式返回19行中的地址 (以地区.因此, 我们提供了有关错误的更多信息, 在这种情况下, 使用自定义错误结构的字段导致错误的半径.
如果半径不是负值, 则此函数计算并返回21行中的区域以及nil错误。
在26行的主函数中, 我们试图找到一个半径为20的圆的面积。由于半径小于零, 将返回一个错误。
我们检查错误是否在27行中, 在下一行中, 我们断言它键入*areaError。如果错误为类型*areaError, 则会在29行中导致错误的半径err.radius, 打印自定义错误消息并从程序返回.
如果断言失败, 我们只需打印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方法中的错误描述。当长度小于零且widthNegative() bool方法在宽度小于零时返回 true 时, lengthNegative() 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, length, width}
}
return length * width, nil
}
上面的rectArea函数检查长度或宽度是否小于零, 如果这样它返回一条错误消息, 否则它会以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)
}
在主函数中, 我们检查4行的错误是否为零。如果它不是零, 我们断言它在下一行中键入*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
我们看到了错误处理教程中描述的三种方法中的两种示例, 以提供有关错误的更多信息。
使用直接比较的第三种方法非常简单。我将把它作为一个练习, 让你弄清楚如何使用这个策略来提供关于我们的自定义错误的更多信息。
这使我们结束了本教程。
下面是我们在本教程中所学到的一个快速回顾,