天天看點

Go error 接口

Go的标準庫函數在發生錯誤時會傳回error類型,比如常用的os.Open函數用來打開檔案,錯誤時傳回error:

func Open(name string) (*File, error)
           

那麼究竟什麼是error類型呢?其實很簡單,error是一個接口,該接口隻聲明了一個方法Error(),傳回值是string類型,用以描述錯誤:

type error interface {
    Error() string
}
           

如何建立自己的error呢?當然可以自己實作這個接口,比如:

package main

import "fmt"

type MyError struct {
    Desc string
}

func (myErr MyError) Error() string {
    return myErr.Desc
}

func doSomething() error {
    // ...
    return MyError{"Logic Error!"}
}

func main() {
    if err := doSomething(); err != nil {
        fmt.Println(err)
    }
}
           

但是更簡單的方法是利用errors包中的New()函數直接建立error對象,errors包的内容僅有如下四行代碼:

package errors
func New(text string) error { return &errorString{text} }
type errorString struct { text string }
func(e *errorString) Error() string { return e.text }
           

可以看到errors包的内部聲明了一個 errorString 結構體,并用其中的text字段來表示錯誤描述,而不是直接用字元串來表示,這樣做的好處是将錯誤描述封裝了起來,防止我們在包外意外地改變錯誤描述。注意實作 error 接口的是 *errorString,而不是 errorString,這是為了讓所有New傳回的error對象都不相等:

一版情況下我們很少調用errors.New()來建立error對象,而是通過fmt包中提供的函數Errorf():

package fmt
import "errors"
func Errorf(format string, args ...interface{}) error {
    return errors.New(Sprintf(format, args...))
}
           

可以看到fmt.Errorf()也是通過errors.New()來建立error對象。

*errorString 可能是最簡單的error對象,但并不是唯一的error對象,比如syscall包中的Errno也是一個實作了error接口的類型。在Unix實作中,Errno的Error()方法通過查找錯誤清單傳回錯誤資訊:

package syscall
type Errno uintptr
var errors = [...]string {
   : "operation not permitted",
   : "no such file or directory",
    // ...
}
func (e Errno) Error() string {
    if <= int(e) && int(e) < len(errors) {
        return errors[e]
    }
    return fmt.Sprintf("errno %d", e)
}
           

參考自《Go程式設計語言》