Go入门指南系列-XIII-I-错误处理
Go 有一个预先定义的 error 接口类型
1 | type error interface { |
错误值用来表示异常状态;我们可以在 5.2 节中看到它的标准用法。处理文件操作的例子可以在 12 章找到;我们将在 15 章看到网络操作的例子。errors
包中有一个 errorString
结构体实现了 error
接口。当程序处于错误状态时可以用 os.Exit(1)
来中止运行。
13.1.1 定义错误
任何时候当你需要一个新的错误类型,都可以用 errors
包(必须先 import
)的 errors.New()
函数接收合适的错误信息来创建,像下面这样:
1 | err := errors.New("math - square root of negative number") |
在示例 13.1 中你可以看到一个简单的用例:
示例 13.1 errors.go:
1 | // errors.go |
可以把它用于计算平方根函数的参数测试:
1 | func Sqrt(f float64) (float64, error) { |
你可以像下面这样调用 Sqrt()
函数:
1 | if f, err := Sqrt(-1); err != nil { |
由于 fmt.Printf
会自动调用 String()
方法 (参见 10.7 节),所以错误信息 “Error: math - square root of negative number
” 会打印出来。通常(错误信息)都会有像 Error:...
这样的前缀,所以你的错误信息不要以大写字母开头(注:英文只有句首单词首字母大写,这里应当是考虑到这一点)。
在大部分情况下自定义错误结构类型很有意义的,可以包含除了(低层级的)错误信息以外的其它有用信息,例如,正在进行的操作(打开文件等),全路径或名字。看下面例子中 os.Open()
操作触发的 PathError
错误:
1 | // PathError records an error and the operation and file path that caused it. |
如果有不同错误条件可能发生,那么对实际的错误使用类型断言或类型判断(type-switch)是很有用的,并且可以根据错误场景做一些补救和恢复操作。
1 | // err != nil |
或:
1 | switch err := err.(type) { |
作为第二个例子考虑用 json
包的情况。当 json.Decode()
在解析 JSON 文档发生语法错误时,指定返回一个 SyntaxError
类型的错误:
1 | type SyntaxError struct { |
在调用代码中你可以像这样用类型断言测试错误是不是上面的类型:
1 | if serr, ok := err.(*json.SyntaxError); ok { |
包也可以用额外的方法 (methods)定义特定的错误,比如 net.Error
:
1 | package net |
在 15.1 节 我们可以看到怎么使用它。
正如你所看到的一样,所有的例子都遵循同一种命名规范:错误类型以 ...Error
结尾,错误变量以 err...
或 Err...
开头或者直接叫 err
或 Err
。
syscall
是低阶外部包,用来提供系统基本调用的原始接口。它们返回封装整数类型错误码的 syscall.Errno
;类型 syscall.Errno
实现了 Error
接口。
大部分 syscall
函数都返回一个结果和可能的错误,比如:
1 | r, err := syscall.Open(name, mode, perm) |
os
包也提供了一套像 os.EINAL
这样的标准错误,它们基于 syscall
错误:
1 | var ( |
13.1.2 用 fmt 创建错误对象
通常你想要返回包含错误参数的更有信息量的字符串,例如:可以用 fmt.Errorf()
来实现:它和 fmt.Printf()
完全一样,接收一个或多个格式占位符的格式化字符串和相应数量的占位变量。和打印信息不同的是它用信息生成错误对象。
比如在前面的平方根例子中使用:
1 | if f < 0 { |
第二个例子:从命令行读取输入时,如果加了 --help
或 -h
标志,我们可以用有用的信息产生一个错误:
1 | if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") { |
链接
- 目录
- 上一节:错误处理与测试
- 下一节:运行时异常和 panic