13.1 錯誤處理

Go 有一個預先定義的 error 接口類型

type error interface {
    Error() string
}

錯誤值用來表示異常狀態;我們可以在 5.2 節中看到它的標準用法。處理文件操作的例子可以在 12 章找到;我們將在 15 章看到網絡操作的例子。errors 包中有一個 errorString 結構體實現了 error 接口。當程序處於錯誤狀態時可以用 os.Exit(1) 來中止運行。

13.1.1 定義錯誤

任何時候當你需要一個新的錯誤類型,都可以用 errors(必須先 import)包的 errors.New 函數接收合適的錯誤信息來創建,像下面這樣:

err := errors.New(“math - square root of negative number”)

在示例 13.1 中你可以看到一個簡單的用例:

示例 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

可以把它用於計算平方根函數的參數測試:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New (“math - square root of negative number”)
    }
   // implementation of Sqrt
}

你可以像下面這樣調用 Sqrt 函數:

if f, err := Sqrt(-1); err != nil {
    fmt.Printf(“Error: %s\n”, err)
}

由於 fmt.Printf 會自動調用 String() 方法 (參見 10.7 節),所以錯誤信息 “Error: math - square root of negative number” 會打印出來。通常(錯誤信息)都會有像 “Error:” 這樣的前綴,所以你的錯誤信息不要以大寫字母開頭。

在大部分情況下自定義錯誤結構類型很有意義的,可以包含除了(低層級的)錯誤信息以外的其它有用信息,例如,正在進行的操作(打開文件等),全路徑或名字。看下面例子中 os.Open 操作觸發的 PathError 錯誤:

// 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)是很有用的,並且可以根據錯誤場景做一些補救和恢復操作。

//  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)
}

作爲第二個例子考慮用 json 包的情況。當 json.Decode 在解析 JSON 文檔發生語法錯誤時,指定返回一個 SyntaxError 類型的錯誤:

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 }

在調用代碼中你可以像這樣用類型斷言測試錯誤是不是上面的類型:

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)定義特定的錯誤,比如 net.Error:

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 函數都返回一個結果和可能的錯誤,比如:

r, err := syscall.Open(name, mode, perm)
if err != 0 {
    fmt.Println(err.Error())
}

os 包也提供了一套像 os.EINAL 這樣的標準錯誤,它們基於 syscall 錯誤:

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() 完全一樣,接收有一個或多個格式佔位符的格式化字符串和相應數量的佔位變量。和打印信息不同的是它用信息生成錯誤對象。

比如在前面的平方根例子中使用:

if f < 0 {
    return 0, fmt.Errorf(“math: square root of negative number %g”, f)
}

第二個例子:從命令行讀取輸入時,如果加了 help 標誌,我們可以用有用的信息產生一個錯誤:

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
}

鏈接

results matching ""

    No results matching ""