什么是错误
在Go中,错误一直是很常见的,错误用内建的error
类型来表示。如果一个函数或方法需要返回错误,按照惯例,会作为最后一个值返回。在处理错误时,通常都是将返回的错误与nil
比较。nil
值表示没有错误发生,而非nil
值表示出现了错误。
错误类型的表示
error
是一个接口类型,定义如下。所有实现该接口的类型都可以当作一个错误类型。Error()
方法给出了错误的描述。
type error interface {
Error() string
}
获取错误详细信息的各种方法
既然error
是一个接口类型,那么它的底层就有对应的具体类型。通过error
的具体类型能获取错误的更多信息。
1. 断言底层结构体类型,使用结构体字段获取更多信息
package main
import (
"fmt"
"os"
)
func main() {
// doc中说明了 Open 函数返回的具体错误类型是 *PathError,PathError是
// 结构体类型,通过声明 Error() string 方法,实现了 error 接口。
file, err := os.Open("/test.txt")
// 如果出现错误,使用类型断言获取 error 接口的底层值。
if err, ok := err.(*os.PathError); ok {
fmt.Println("File at path", err.Path, "failed to open") // 获取指定字段
return
}
fmt.Println(file.Name(), "opened successfully")
// Output: File at path /test.txt failed to open
}
2. 断言底层结构体类型,调用方法获取更多信息
package main
import (
"fmt"
"net"
)
func main() {
// 试图获取域名 golangbot123.com 的 ip
addr, err := net.LookupHost("golangbot123.com")
if err, ok := err.(*net.DNSError); ok {
if err.Timeout() {
fmt.Println("operation timed out")
} else if err.Temporary() {
fmt.Println("temporary error")
} else {
fmt.Println("generic error:", err)
}
return
}
fmt.Println(addr)
// Output: generic error: lookup golangbot123.com: no such host
}
3. 直接比较
还有一种获取错误更多信息的方式,是与error
类型的变量直接比较。
package main
import (
"fmt"
"path/filepath"
)
func main() {
// Glob 函数如果出错会返回一个错误 ErrBadPattern,
// 而 filepath.ErrBadPattern 为包中定义的变量。
// 所以可以直接进行比较。
files, err := filepath.Glob("[")
if err != nil && err == filepath.ErrBadPattern {
fmt.Println(err)
return
}
fmt.Println("matched files", files)
// Output: syntax error in pattern
}
自定义错误
1. 使用标准库中的方法来创建自定义错误
- 创建自定义错误最简单的方法是使用
errors
包中的New
函数。 fmt
包中的Errorf
函数可以根据格式说明符,规定错误的格式,然后返回一个错误字符串。其实等同于errors.New(fmt.Sprintf(format, a...))
。
2. 使用结构体类型和字段提供错误的更多信息
package main
import (
"fmt"
"math"
)
type areaError struct {
err string // 存储了实际的错误信息
radius float64 // 存储了与错误有关的半径
}
// 实现 error 接口
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 rectangle %0.2f", area)
// Output: Radius -20.00 is less than zero
}
3. 使用结构体类型的方法来提供错误的更多信息
package main
import "fmt"
type areaError struct {
err string // 错误描述字段
length float64 // 引发错误的长
width float64 // 引发错误的宽
}
// 实现 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)
// Output:
// error: length -5.00 is less than zero
// error: width -9.00 is less than zero
}
reference:
https://studygolang.com/articles/12724
https://studygolang.com/articles/12784