- 什么是错误?
- 错误的表示
- 获取错误详细信息的各种方法
- 不能忽视错误
先看一个例子
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("/test.txt")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Name(), "opened successfully")
}
打开文件的函数原型如下
func Open(name string) (file *File, err error)
当我试图打开一个不存在的文件时,err 返回一个不为nil的值,如果文件存在err就会返回nil
那么 error 到底是什么?
type error interface {
Error() string
}
那么我们就知道了 error 就是就是一个抽象的接口,那么open 方法中的error 对应的底层结构体是什么
断言底层类型,获取更多属性
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
所以我们可以将error 类型转换成 pathError 然后查看里面的具体信息
package main
import (
"fmt"
"os"
)
func main() {
f, err := os.Open("/test.txt")
if err, ok := err.(*os.PathError); ok {
fmt.Println("File at path", err.Path, "failed to open")
return
}
fmt.Println(f.Name(), "opened successfully")
}
断言底层类型,调用方法获取更多信息
比如请求网络,发生错误,那是由于网路超时 还是 其他原因导致的失败,我们怎么知道呢?
type DNSError struct {
...
}
func (e *DNSError) Error() string {
...
}
func (e *DNSError) Timeout() bool {
...
}
func (e *DNSError) Temporary() bool {
...
}
看下面代码
package main
import (
"fmt"
"net"
)
func main() {
addr, err := net.LookupHost("niubi345.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)
}
这样我们就可根据不同的方法返回值来进一步判断错误类型了
直接比较
第三种获取错误的更多信息的方式,是与 error 类型的变量直接比较
看一个例子
package main
import (
"fmt"
"path/filepath"
)
func main() {
files, error := filepath.Glob("[")
if error != nil && error == filepath.ErrBadPattern {
fmt.Println(error)
return
}
fmt.Println("matched files", files)
}
filepath.ErrBadPattern 是filepath 包里面的一个公开变量 如下
var ErrBadPattern = errors.New("syntax error in pattern")
errors 是什么?
errors 包中通过new 创建了一个结构体,这个结构体实现了错误接口Error()
不可忽略错误
package main
import (
"fmt"
"path/filepath"
)
func main() {
files, _ := filepath.Glob("[")
fmt.Println("matched files", files)
}
我们已经从前面的示例知道了这个模式是错误的。在第 9 行,通过使用 _ 空白标识符,我忽略了 Glob 函数返回的错误。我在第 10 行简单打印了所有匹配的文件。该程序会输出
自定义error 错误
// 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
}
*fmt.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)
}
使用结构体类型和字段提供错误的更多信息
定义错类类型结构体
type areaError struct {
err string
radius float64
}
func (e *areaError) Error() string {
return fmt.Sprintf("radius %0.2f: %s", e.radius, e.err)
}
注意 我们是在areaError 实现的方法,所以areaError 是实现了接口,但是areaError 没有实现 所以我们使用的时候,一定要传递*areaError
如下
func circleArea(radius float64) (float64, error) {
if radius < 0 {
return 0, &areaError{"radius is negative", radius}
}
return math.Pi * radius * radius, nil
}
解析错误 需要进行数据转换
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
}
让错误类型携带更多的信息
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
}