|
|
@@ -65,4 +65,137 @@ if f, err := Sqrt(-1); err != nil {
|
|
|
|
|
|
由于 `fmt.Printf` 会自动调用 `String()` 方法 (参见 10.7 节),所以错误信息“Error: math - square root of negative number”会打印出来。通常(错误信息)都会有像“Error:”这样的前缀,所以你的错误信息不要以大写字母开头。
|
|
|
|
|
|
+在大部分情况下自定义错误结构类型是有趣的,可以包含除了(低层级的)错误信息以外的其它有用信息,例如,正在进行的操作(打开文件等),全路径或名字。看下面例子中 os.Open 操作触发的 PathError 错误:
|
|
|
|
|
|
+```go
|
|
|
+// PathError records an error and the operation and file path that caused it.
|
|
|
+ type PathError struct {
|
|
|
+ Op string // “open”, “unlink”, etc.
|
|
|
+ Path string // The associated file.
|
|
|
+ Err error // Returned by the system call.
|
|
|
+}
|
|
|
+
|
|
|
+func (e *PathError) String() string {
|
|
|
+ return e.Op + “ ” + e.Path + “: “+ e.Err.Error()
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+如果会有不同的错误条件可能发生,那么对实际的错误使用类型断言或类型判断(type-switch)是很有用的,并且可以根据错误情况做一些补救和恢复操作。
|
|
|
+
|
|
|
+```go
|
|
|
+// err != nil
|
|
|
+if e, ok := err.(*os.PathError); ok {
|
|
|
+ // remedy situation
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+或:
|
|
|
+
|
|
|
+```go
|
|
|
+switch err := err.(type) {
|
|
|
+case ParseError:
|
|
|
+ PrintParseError(err)
|
|
|
+case PathError:
|
|
|
+ PrintPathError(err)
|
|
|
+... default:
|
|
|
+ fmt.Printf(“Not a special error, just %s\n”, err)
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+作为第二个例子考虑用 json 包的情况。当 json.Decode 在解析 JSON 文档发生语法错误时,指定返回一个 SyntaxError 类型的错误:
|
|
|
+
|
|
|
+```go
|
|
|
+type SyntaxError struct {
|
|
|
+ msg string // description of error
|
|
|
+// error occurred after reading Offset bytes, from which line and columnnr can be obtained
|
|
|
+ Offset int64
|
|
|
+}
|
|
|
+
|
|
|
+func (e *SyntaxError) String() string { return e.msg }
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+在调用代码中你可以像这样用类型断言测试错误是不是上面的类型:
|
|
|
+
|
|
|
+```go
|
|
|
+if serr, ok := err.(*json.SyntaxError); ok {
|
|
|
+ line, col := findLine(f, serr.Offset)
|
|
|
+ return fmt.Errorf(“%s:%d:%d: %v”, f.Name(), line, col, err)
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+包也可以用别的方法定义指定的错误,比如 net.Errot:
|
|
|
+
|
|
|
+```go
|
|
|
+package net
|
|
|
+type Error interface {
|
|
|
+ Timeout() bool // Is the error a timeout?
|
|
|
+ Temporary() bool // Is the error temporary?
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+在 15.1 节 我们可以看到怎么使用它。
|
|
|
+
|
|
|
+正如你所看到的一样,所有的例子都遵循同一种命名规范:错误类型以“Error”结尾,错误变量以“err”或“Err”开头。
|
|
|
+
|
|
|
+syscall 是低阶外部包,用来提供基本系统调用的原始接口。它们返回整数的错误码;类型 syscall.Errno 实现了 Error 接口。
|
|
|
+
|
|
|
+大部分 syscall 函数都返回一个结果和可能的错误,比如:
|
|
|
+
|
|
|
+```go
|
|
|
+r, err := syscall.Open(name, mode, perm)
|
|
|
+if err != 0 {
|
|
|
+ fmt.Println(err.Error())
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+os 包也提供了一套像 os.EINAL 这样的标准错误,它们基于 syscall 错误:
|
|
|
+
|
|
|
+```go
|
|
|
+var (
|
|
|
+ EPERM Error = Errno(syscall.EPERM)
|
|
|
+ ENOENT Error = Errno(syscall.ENOENT)
|
|
|
+ ESRCH Error = Errno(syscall.ESRCH)
|
|
|
+ EINTR Error = Errno(syscall.EINTR)
|
|
|
+ EIO Error = Errno(syscall.EIO)
|
|
|
+ ...
|
|
|
+)
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+## 13.1.2 用 fmt 创建错误对象
|
|
|
+
|
|
|
+通常你想要返回用包含错误参数的更有信息量的字符串,例如:可以用 `fmt.Errorf()` 实现:它和 fmt.Printf() 完全一样,接收一个有一个或多个格式占位符的格式化字符串和相应数量的占位变量。和打印信息不同的是它用信息生成错误对象。
|
|
|
+
|
|
|
+在上面的平方根例子中使用:
|
|
|
+
|
|
|
+```go
|
|
|
+if f < 0 {
|
|
|
+ return 0, fmt.Errorf(“math: square root of negative number %g”, f)
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+第二个例子:从命令行读取输入时,如果加了 help 标志,我们就产生用有用的信息产生一个错误:
|
|
|
+
|
|
|
+```go
|
|
|
+if len(os.Args) > 1 && (os.Args[1] == “-h” || os.Args[1] == “--help”) {
|
|
|
+ err = fmt.Errorf(“usage: %s infile.txt outfile.txt”, filepath.Base(os.
|
|
|
+ Args[0]))
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+## 链接
|
|
|
+
|
|
|
+- [目录](directory.md)
|
|
|
+- 上一节:[错误处理与测试](12.12.md)
|
|
|
+- 下一节:[运行时异常和 panic](13.2.md)
|