鍍金池/ 教程/ GO/ 13.1 錯(cuò)誤處理
4.7 strings 和 strconv 包
13.6 啟動(dòng)外部命令和程序
?# 11.4 類型判斷:type-switch
12.1 讀取用戶的輸入
10.6 方法
12.2 文件讀寫(xiě)
13 錯(cuò)誤處理與測(cè)試
9.3 鎖和 sync 包
12.3 文件拷貝
?# 11.7 第一個(gè)例子:使用 Sorter 接口排序
?# 11.5 測(cè)試一個(gè)值是否實(shí)現(xiàn)了某個(gè)接口
6.4 defer 和追蹤
12.10 XML 數(shù)據(jù)格式
13.10 性能調(diào)試:分析并優(yōu)化 Go 程序
?# 11.1 接口是什么
2.2 Go 環(huán)境變量
2.6 安裝目錄清單
2.5 在 Windows 上安裝 Go
11.11 Printf 和反射
1.2 語(yǔ)言的主要特性與發(fā)展的環(huán)境和影響因素
9.0 包(package)
7.4 切片重組(reslice)
13.2 運(yùn)行時(shí)異常和 panic
10.2 使用工廠方法創(chuàng)建結(jié)構(gòu)體實(shí)例
12.8 使用接口的實(shí)際例子:fmt.Fprintf
2.4 在 Mac OS X 上安裝 Go
3.8 Go 性能說(shuō)明
7.2 切片
8.0 Map
3.1 Go 開(kāi)發(fā)環(huán)境的基本要求
5.6 標(biāo)簽與 goto
6.10 使用閉包調(diào)試
9.5 自定義包和可見(jiàn)性
4.3 常量
?# 11.2 接口嵌套接口
6.5 內(nèi)置函數(shù)
前言
10.8 垃圾回收和 SetFinalizer
2.8 Go 解釋器
13.7 Go 中的單元測(cè)試和基準(zhǔn)測(cè)試
6.8 閉包
4.9 指針
13.1 錯(cuò)誤處理
10.1 結(jié)構(gòu)體定義
5.1 if-else 結(jié)構(gòu)
6.6 遞歸函數(shù)
9.9 通過(guò) Git 打包和安裝
2.7 Go 運(yùn)行時(shí)(runtime)
10.7 類型的 String() 方法和格式化描述符
3.7 其它工具
9.6 為自定義包使用 godoc
11.12 接口與動(dòng)態(tài)類型
13.3 從 panic 中恢復(fù)(Recover)
10.3 使用自定義包中的結(jié)構(gòu)體
11.14 結(jié)構(gòu)體、集合和高階函數(shù)
3.6 生成代碼文檔
9.2 regexp 包
4.1 文件名、關(guān)鍵字與標(biāo)識(shí)符
?# 11.6 使用方法集與接口
7.0 數(shù)組與切片
7.1 聲明和初始化
12.11 用 Gob 傳輸數(shù)據(jù)
5.5 Break 與 continue
1.1 起源與發(fā)展
?# 11 接口(Interfaces)與反射(reflection)
6.9 應(yīng)用閉包:將函數(shù)作為返回值
4.2 Go 程序的基本結(jié)構(gòu)和要素
8.6 將 map 的鍵值對(duì)調(diào)
6.11 計(jì)算函數(shù)執(zhí)行時(shí)間
5.0 控制結(jié)構(gòu)
10.5 匿名字段和內(nèi)嵌結(jié)構(gòu)體
4.6 字符串
3.0 編輯器、集成開(kāi)發(fā)環(huán)境與其它工具
13.8 測(cè)試的具體例子
7.6 字符串、數(shù)組和切片的應(yīng)用
8.4 map 類型的切片
3.9 與其它語(yǔ)言進(jìn)行交互
7.3 For-range 結(jié)構(gòu)
9.7 使用 go install 安裝自定義包
6.0 函數(shù)
9.8 自定義包的目錄結(jié)構(gòu)、go install 和 go test
6.3 傳遞變長(zhǎng)參數(shù)
13.9 用(測(cè)試數(shù)據(jù))表驅(qū)動(dòng)測(cè)試
11.9 空接口
8.1 聲明、初始化和 make
6.2 函數(shù)參數(shù)與返回值
9.11 在 Go 程序中使用外部庫(kù)
3.3 調(diào)試器
4.5 基本類型和運(yùn)算符
?# 11.8 第二個(gè)例子:讀和寫(xiě)
12.5 用 buffer 讀取文件
總結(jié):Go 中的面向?qū)ο?/span>
11.10 反射包
12.7 用 defer 關(guān)閉文件
9.4 精密計(jì)算和 big 包
4.4 變量
6.1 介紹
13.4 自定義包中的錯(cuò)誤處理和 panicking
12.4 從命令行讀取參數(shù)
9.10 Go 的外部包和項(xiàng)目
8.3 for-range 的配套用法
3.5 格式化代碼
10.4 帶標(biāo)簽的結(jié)構(gòu)體
7.5 切片的復(fù)制與追加
?# 11.3 類型斷言:如何檢測(cè)和轉(zhuǎn)換接口變量的類型
5.4 for 結(jié)構(gòu)
4.8 時(shí)間和日期
2.3 在 Linux 上安裝 Go
12 讀寫(xiě)數(shù)據(jù)
6.12 通過(guò)內(nèi)存緩存來(lái)提升性能
9.1 標(biāo)準(zhǔn)庫(kù)概述
12.6 用切片讀寫(xiě)文件
10 結(jié)構(gòu)(struct)與方法(method)
8.5 map 的排序
12.9 JSON 數(shù)據(jù)格式
13.5 一種用閉包處理錯(cuò)誤的模式
3.2 編輯器和集成開(kāi)發(fā)環(huán)境
12.12 Go 中的密碼學(xué)
5.2 測(cè)試多返回值函數(shù)的錯(cuò)誤
6.7 將函數(shù)作為參數(shù)
8.2 測(cè)試鍵值對(duì)是否存在及刪除元素
3.4 構(gòu)建并運(yùn)行 Go 程序
2.1 平臺(tái)與架構(gòu)
5.3 switch 結(jié)構(gòu)

13.1 錯(cuò)誤處理

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)行。

13.1.1 定義錯(cuò)誤

任何時(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)
    ...
)

13.1.2 用 fmt 創(chuàng)建錯(cuò)誤對(duì)象

通常你想要返回包含錯(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
}

鏈接