Go 有一個(gè)預(yù)先定義的 error 接口類型
type error interface {
Error() string
}
錯(cuò)誤值用來(lái)表示異常狀態(tài);我們可以在 5.2 節(jié)中看到它的標(biāo)準(zhǔn)用法。處理文件操作的例子可以在 12 章找到;我們將在 15 章看到網(wǎng)絡(luò)操作的例子。errors 包中有一個(gè) errorString 結(jié)構(gòu)體實(shí)現(xiàn)了 error 接口。當(dāng)程序處于錯(cuò)誤狀態(tài)時(shí)可以用 os.Exit(1)
來(lái)中止運(yùn)行。
任何時(shí)候當(dāng)你需要一個(gè)新的錯(cuò)誤類型,都可以用 errors
(必須先 import)包的 errors.New
函數(shù)接收合適的錯(cuò)誤信息來(lái)創(chuàng)建,像下面這樣:
err := errors.New("math - square root of negative number")
在示例 13.1 中你可以看到一個(gè)簡(jiǎn)單的用例:
示例 13.1 errors.go:
// errors.go
package main
import (
"errors"
"fmt"
)
var errNotFound error = errors.New("Not found error")
func main() {
fmt.Printf("error: %v", errNotFound)
}
// error: Not found error
可以把它用于計(jì)算平方根函數(shù)的參數(shù)測(cè)試:
func Sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New ("math - square root of negative number")
}
// implementation of Sqrt
}
你可以像下面這樣調(diào)用 Sqrt 函數(shù):
if f, err := Sqrt(-1); err != nil {
fmt.Printf("Error: %s\n", err)
}
由于 fmt.Printf
會(huì)自動(dòng)調(diào)用 String()
方法 (參見(jiàn) 10.7 節(jié)),所以錯(cuò)誤信息 “Error: math - square root of negative number” 會(huì)打印出來(lái)。通常(錯(cuò)誤信息)都會(huì)有像 “Error:” 這樣的前綴,所以你的錯(cuò)誤信息不要以大寫(xiě)字母開(kāi)頭。
在大部分情況下自定義錯(cuò)誤結(jié)構(gòu)類型很有意義的,可以包含除了(低層級(jí)的)錯(cuò)誤信息以外的其它有用信息,例如,正在進(jìn)行的操作(打開(kāi)文件等),全路徑或名字??聪旅胬又?os.Open 操作觸發(fā)的 PathError 錯(cuò)誤:
// 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()
}
如果有不同錯(cuò)誤條件可能發(fā)生,那么對(duì)實(shí)際的錯(cuò)誤使用類型斷言或類型判斷(type-switch)是很有用的,并且可以根據(jù)錯(cuò)誤場(chǎng)景做一些補(bǔ)救和恢復(fù)操作。
// err != nil
if e, ok := err.(*os.PathError); ok {
// remedy situation
}
或:
switch err := err.(type) {
case ParseError:
PrintParseError(err)
case PathError:
PrintPathError(err)
...
default:
fmt.Printf("Not a special error, just %s\n", err)
}
作為第二個(gè)例子考慮用 json 包的情況。當(dāng) json.Decode 在解析 JSON 文檔發(fā)生語(yǔ)法錯(cuò)誤時(shí),指定返回一個(gè) SyntaxError 類型的錯(cuò)誤:
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 }
在調(diào)用代碼中你可以像這樣用類型斷言測(cè)試錯(cuò)誤是不是上面的類型:
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)
}
包也可以用額外的方法(methods)定義特定的錯(cuò)誤,比如 net.Error:
package net
type Error interface {
Timeout() bool // Is the error a timeout?
Temporary() bool // Is the error temporary?
}
在 15.1 節(jié) 我們可以看到怎么使用它。
正如你所看到的一樣,所有的例子都遵循同一種命名規(guī)范:錯(cuò)誤類型以 “Error” 結(jié)尾,錯(cuò)誤變量以 “err” 或 “Err” 開(kāi)頭。
syscall 是低階外部包,用來(lái)提供系統(tǒng)基本調(diào)用的原始接口。它們返回整數(shù)的錯(cuò)誤碼;類型 syscall.Errno 實(shí)現(xiàn)了 Error 接口。
大部分 syscall 函數(shù)都返回一個(gè)結(jié)果和可能的錯(cuò)誤,比如:
r, err := syscall.Open(name, mode, perm)
if err != 0 {
fmt.Println(err.Error())
}
os 包也提供了一套像 os.EINAL 這樣的標(biāo)準(zhǔn)錯(cuò)誤,它們基于 syscall 錯(cuò)誤:
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)
...
)
通常你想要返回包含錯(cuò)誤參數(shù)的更有信息量的字符串,例如:可以用 fmt.Errorf()
來(lái)實(shí)現(xiàn):它和 fmt.Printf() 完全一樣,接收一個(gè)或多個(gè)格式占位符的格式化字符串和相應(yīng)數(shù)量的占位變量。和打印信息不同的是它用信息生成錯(cuò)誤對(duì)象。
比如在前面的平方根例子中使用:
if f < 0 {
return 0, fmt.Errorf("math: square root of negative number %g", f)
}
第二個(gè)例子:從命令行讀取輸入時(shí),如果加了 help 標(biāo)志,我們可以用有用的信息產(chǎn)生一個(gè)錯(cuò)誤:
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
}