1. 程式人生 > >GO學習筆記——錯誤處理(22)

GO學習筆記——錯誤處理(22)

C++中通過異常機制來處理一些異常的情況,這是C++錯誤處理的方式。

GO語言中,有專門的error型別來表示錯誤,這也是一種內建型別,它一般作為某些函式返回引數的第二個引數,來判斷函式的呼叫是否出錯,並可以將出錯原因賦值給error型別的變數。

func main() {
	f,err := os.Open("test.txt")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(f.Name(),"open successfully!")
}

執行結果

open test.txt: The system cannot find the file specified.

當我們試著去開啟一個不存在的檔案時,該函式其實返回的是兩個值,一個是File型別,一個是error內建型別。

如果函式呼叫成功,也就是檔案開啟成功,期間沒有出現任何的錯誤,那麼返回的error就是nil,否則如果出錯,返回的error就不是nil。

所以在這裡也可以看到,error型別的預設零值為nil。

上述程式碼中我們將err和nil進行比較,這也是我們一貫的用法。因為只要函式呼叫沒有出錯,那麼err的值一定是nil;只要函式調用出錯了,那麼err的值一定是一個不等於nil的值。

error內建型別內部組成

我們先來看一下上述open函式的底層模型

func Open(name string) (*File, error) {
	return OpenFile(name, O_RDONLY, 0)
}

可以看到該函式的第二個返回值是一個error型別,再來看一下這個error型別的底層

type error interface {
	Error() string
}

它其實就是一個介面,這個介面內部實現了一個方法Error,它的返回值是string,所以對於上面的程式碼, 我們還可以這麼改。

func main() {
	f,err := os.Open("test.txt")
	if err != nil {
		fmt.Println("Error:",err.Error())
		return
	}
	fmt.Println(f.Name(),"open successfully!")
}

因為err是一個介面型別,所以我們也可以用介面的方式來呼叫,執行結果如下

Error: open test.txt: The system cannot find the file specified.

當然我們沒必要這麼麻煩,還是像之前那樣直接列印err就可以了,編譯器會自動幫我們找到err.Error()。



從錯誤獲取更多資訊

那麼我們的error型別僅僅只有一個Error方法,我們能獲取的只是出錯原因,這樣我們獲取到的資訊就太少了。有時候還想獲取一些別的資訊,怎麼辦?

其實open函式原始碼中的一行註釋已經告訴我們方法了。

// If there is an error, it will be of type *PathError.

如果有錯誤,那麼它將是型別*PathError。

這個PathError到底是什麼?

// PathError records an error and the operation and file path that caused it.
type PathError struct {
	Op   string
	Path string
	Err  error
}

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }

這是一個結構體,它實現了Error方法,因此PathError型別實現了error介面。我們也可以直接呼叫。

通過這個結構體,我們就可以間接地獲取到更多資訊。先來看一下之前的出錯原因

 open test.txt: The system cannot find the file specified

對應著上面的PathError型別實現的Error方法,裡面有return語句,我們可以發現,Op型別其實就是open,就是操作方式;Path型別其實是test.txt,就是檔案路徑;之後還有一個Err的error型別,列印的是The system cannot find the file specified。

因此,它實現的方式其實就是將三個變數組合起來組合成了一個完整的出錯原因語句。如果我們想要得到完整語句的每一部分,也是可以的。

func main() {
	f,err := os.Open("test.txt")
	if err != nil {
		if pathError,ok := err.(*os.PathError); ok != false {
			fmt.Println(pathError.Op)
			fmt.Println(pathError.Path)
			fmt.Println(pathError.Err.Error())
		}
		return
	}
	fmt.Println(f.Name(),"open successfully!")
}

執行結果

open
test.txt
The system cannot find the file specified.

當然,這僅僅是一個PathError型別表示路徑出錯,還有別的出錯型別,它們也是實現了Error方法,從而實現了error介面,來起到出錯處理的目的,有些型別不僅實現了Error方法,還實現了別的一些方法來獲取別的資訊。

自定義錯誤型別

使用New函式可以自定義錯誤型別

func main() {
	err := errors.New("I am an error")
	if err != nil {
		fmt.Println(err)
	}
}

執行結果

I am an error