Golang errors包快速上手
在 Golang 中,errors 包是用于處理錯誤的標(biāo)準(zhǔn)庫, errors 包提供的功能比較簡單,使用起來非常方便。
接下來具體講解一下 errors 包提供的變量、類型和函數(shù)。
1.變量
errors 包只定義了一個全局變量 ErrUnsupported。
var ErrUnsupported = New("unsupported operation")
ErrUnsupported 表示請求的操作不能執(zhí)行,因?yàn)樗皇苤С?。例如,調(diào)用os.Link()當(dāng)使用的文件系統(tǒng)不支持硬鏈接時。
函數(shù)和方法不應(yīng)該返回這個錯誤,而應(yīng)該返回一個包含適當(dāng)上下文的錯誤,滿足:
errors.Is(err, errors.ErrUnsupported)
要么直接包裝 ErrUnsupported,要么實(shí)現(xiàn)一個 Is 方法。
函數(shù)和方法應(yīng)該說明何種情況下會返回包含 ErrUnsupported 的錯誤。
2.類型
error 是一個內(nèi)建的接口類型,任何類型只要實(shí)現(xiàn)了 Error() string 方法,就實(shí)現(xiàn)了 error 接口,這意味著該類型的實(shí)例可以被當(dāng)作一個 error 來處理。
// The error built-in interface type is the conventional interface for // representing an error condition, with the nil value representing no error. type error interface { Error() string }
3.函數(shù)
3.1 New
errors.New 用于創(chuàng)建一個新的錯誤對象。它接收一個字符串作為錯誤消息,并返回一個錯誤對象。
func New(text string) error
我們可以看下其具體實(shí)現(xiàn):
// New returns an error that formats as the given text. // Each call to New returns a distinct error value even if the text is identical. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s }
可以看到,New 返回的是實(shí)現(xiàn)了 error 接口的具體類型 *errorString。
3.2 Is
簡介
errors.Is 函數(shù)是一個用于錯誤處理的核心工具,用于檢查錯誤鏈(error chain)中是否存在某個特定的錯誤實(shí)例。
它是 Go 1.13 版本引入的錯誤處理增強(qiáng)功能之一,與 errors.As 和 errors.Unwrap 共同提供了更靈活的錯誤處理機(jī)制。
函數(shù)簽名
func Is(err, target error) bool
- err: 要檢查的錯誤。
- target: 我們想要確認(rèn)的錯誤。
- 返回值: 如果錯誤鏈中任一錯誤與目標(biāo)錯誤相同,則返回 true。
核心功能
- 遞歸解包錯誤鏈errors.Is 會通過 Unwrap() 或Unwrap() []error方法逐層解包錯誤鏈,檢查每一層錯誤是否與 target 匹配。
- 值相等性檢查檢查錯誤的“值”是否與 target 相等。默認(rèn)使用 == 操作符比較,但若錯誤類型實(shí)現(xiàn)了 Is(error) bool 方法,則優(yōu)先調(diào)用該方法進(jìn)行判斷(允許自定義相等邏輯)。
- 支持自定義錯誤匹配邏輯如果自定義錯誤類型需要定義特殊的相等規(guī)則(例如比較結(jié)構(gòu)體字段而非指針地址),可以實(shí)現(xiàn) Is(error) bool 方法。
示例代碼
package main import ( "errors" "fmt" ) var ErrNotFound = errors.New("not found") func main() { err := fmt.Errorf("context: %w", ErrNotFound) if errors.Is(err, ErrNotFound) { fmt.Println("錯誤鏈中包含 ErrNotFound") } errNotFoundNew := errors.New("not found") fmt.Println(errors.Is(errNotFoundNew, ErrNotFound)) // false }
運(yùn)行輸出:
錯誤鏈中包含 ErrNotFound
false
因?yàn)?err 是基于 ErrNotFound 包裝出來的,所以 Is 判斷返回 true。
因?yàn)?errNotFoundNew 是一個新的 error,雖然錯誤內(nèi)容與 ErrNotFound 相同,但是二者是兩個獨(dú)立的 error 對象,所以 Is 判斷返回 false。
使用場景
- 檢查預(yù)定義錯誤例如判斷錯誤是否為 io.EOF 或 os.ErrNotExist:
if errors.Is(err, io.EOF) { // 處理文件結(jié)束邏輯 }
- 自定義錯誤匹配當(dāng)需要根據(jù)錯誤的某些字段(而非指針地址)匹配時,自定義 Is 方法:
type ValidationError struct { Field string } func (e *ValidationError) Is(target error) bool { t, ok := target.(*ValidationError) return ok && e.Field == t.Field }
- 處理多層錯誤鏈自動遍歷包裹錯誤(如 fmt.Errorf + %w 生成的錯誤鏈):
err := fmt.Errorf("layer2: %w", fmt.Errorf("layer1: %w", originalErr)) if errors.Is(err, originalErr) { // 直接檢查最底層錯誤 // 匹配成功 }
與 == 操作符的區(qū)別:
- 默認(rèn)行為:如果錯誤類型未實(shí)現(xiàn) Is 方法,errors.Is 默認(rèn)使用 == 比較錯誤值和 target。但對于指針類型的錯誤(如 &MyError{}),== 比較的是指針地址而非值內(nèi)容。
- 自定義邏輯:通過實(shí)現(xiàn) Is 方法,可以控制錯誤的匹配邏輯(如比較結(jié)構(gòu)體字段)。
注意事項
優(yōu)先實(shí)現(xiàn) Is 方法:如果自定義錯誤需要支持值匹配(而非指針匹配),必須實(shí)現(xiàn) Is 方法。
target可以是nil:如果 target 為 nil,errors.Is 僅在 err 也為 nil 時返回 true。
性能錯誤鏈較長時,遞歸解包可能導(dǎo)致輕微性能開銷,但通??珊雎?。
小結(jié)
errors.Is 是 Go 錯誤處理的基石之一,它通過遞歸解包錯誤鏈,提供了一種安全、統(tǒng)一的方式來檢查特定錯誤的存在。結(jié)合以下實(shí)踐可最大化其效用:
- 對需要值匹配的自定義錯誤實(shí)現(xiàn) Is 方法。
- 優(yōu)先使用 errors.Is 而非 == 直接比較錯誤(確保兼容錯誤鏈)。
- 與 errors.As 分工:Is 用于值匹配,As 用于類型提取。
3.3 As
簡介
Golang 中的 errors.As 函數(shù)是一個用于錯誤處理的重要工具,它提供了一種類型安全的方式,用于檢查錯誤樹中是否存在某個特定類型的錯誤,并提取該類型的錯誤實(shí)例。
它是 Go 1.13 引入的錯誤處理增強(qiáng)功能之一,與 errors.Is 和 errors.Unwrap 共同構(gòu)成了更靈活的錯誤處理機(jī)制。
函數(shù)簽名
func As(err error, target any) bool
- err: 要檢查的錯誤樹。
- target: 一個指向目標(biāo)類型的指針,用于存儲結(jié)果。
- 返回值: 如果錯誤樹中存在指定的類型,則返回 true,并且 target 參數(shù)會被設(shè)置為相應(yīng)的錯誤值。
核心功能
errors.As 會遞歸遍歷錯誤樹(通過 Unwrap() 或 Unwrap() []error 方法解包錯誤),檢查是否存在與 target 類型匹配的錯誤。如果找到,它會將匹配的錯誤值賦值給 target,并返回 true。
如果錯誤的具體值可分配給 target 所指向的值,或者如果錯誤有一個方法As(any) bool使得As(target)返回true,則錯誤匹配 target。在后一種情況下,As 方法負(fù)責(zé)將錯誤值設(shè)置到 target。
與 errors.Is 的區(qū)別:
- errors.Is(err, target):檢查錯誤樹中是否存在值等于 target 的錯誤(值比較)。
- errors.As(err, &target):檢查錯誤樹中是否存在類型與 target 匹配的錯誤(類型斷言)。
示例代碼
package main import ( "errors" "fmt" ) // 自定義錯誤類型 type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf("code: %d, msg: %s", e.Code, e.Message) } func main() { err := &MyError{Code: 404, Message: "Not Found"} wrappedErr := fmt.Errorf("wrapper: %w", err) // 包裹錯誤 var myErr *MyError if errors.As(wrappedErr, &myErr) { fmt.Println("Found MyError:", myErr.Code, myErr.Message) // 輸出: Found MyError: 404 Not Found } }
在這個例子中:
- 自定義錯誤類型 MyError 實(shí)現(xiàn)了 error 接口。
- 通過 fmt.Errorf 和 %w 包裹原始錯誤,形成錯誤樹。
- errors.As 檢查包裹后的錯誤樹,找到 MyError 類型的錯誤實(shí)例,并將其賦值給 myErr。
使用場景
提取特定錯誤類型的詳細(xì)信息當(dāng)錯誤類型包含額外字段(如錯誤碼、上下文信息)時,可以通過 errors.As 提取這些信息。
處理標(biāo)準(zhǔn)庫中的錯誤類型例如,檢查一個錯誤是否是 os.PathError 類型,以獲取文件路徑相關(guān)的詳細(xì)信息:
var pathErr *os.PathError if errors.As(err, &pathErr) { fmt.Println("Failed at path:", pathErr.Path) }
- 多層級錯誤解包無需手動調(diào)用 Unwrap() 遍歷錯誤鏈,errors.As 會自動處理嵌套錯誤。
注意事項
- target 必須是指針target 必須是一個指向接口或具體類型的指針。例如:
var target *MyError // 正確(具體類型指針) var target error = &MyError{} // 正確(接口類型指針)
類型必須匹配target 的類型需要與錯誤鏈中某個錯誤的具體類型完全一致(或接口類型)。
性能如果錯誤樹非常長,errors.As 可能需要遍歷整個樹,但實(shí)際場景中性能影響通??珊雎?。
小結(jié)
errors.As 是 Go 錯誤處理中類型斷言的最佳實(shí)踐,它簡化了從錯誤鏈中提取特定類型錯誤的操作。結(jié)合 errors.Is 和錯誤包裹(fmt.Errorf + %w),可以構(gòu)建清晰、可維護(hù)的錯誤處理邏輯。
3.4 Unwrap
簡介
errors.Unwrap 是 Go 1.13 引入的錯誤處理函數(shù),用于獲取被包裝(wrapped)錯誤的原始錯誤。它是 Go 錯誤處理機(jī)制中錯誤鏈(error chain)支持的核心部分。
函數(shù)簽名
func Unwrap(err error) error
- 解包錯誤:如果 err 實(shí)現(xiàn)了
Unwrap() error
方法,則返回該方法的結(jié)果。 - 無包裝時:如果錯誤不支持解包或已經(jīng)是底層錯誤,返回 nil。
- 簡單直接:僅解包一層,不會遞歸解包整個錯誤鏈。
核心功能
- 解包錯誤鏈用于從包裹錯誤(如通過
fmt.Errorf
和%w
生成的錯誤)中提取下一層錯誤。 - 支持自定義錯誤類型。若自定義錯誤類型實(shí)現(xiàn)了
Unwrap() error
方法,errors.Unwrap
可自動調(diào)用它來解包錯誤。
使用示例
err := fmt.Errorf("wrapper: %w", io.EOF) unwrapped := errors.Unwrap(err) fmt.Println(unwrapped == io.EOF) // 輸出: true fmt.Println(unwrapped) // 輸出: EOF errors.Unwrap 通常與 fmt.Errorf 的 %w 動詞配合使用: func process() error { if err := step1(); err != nil { return fmt.Errorf("step1 failed: %w", err) } // ... } err := process() if errors.Unwrap(err) != nil { // 處理原始錯誤 }
- 標(biāo)準(zhǔn)接口:要求被解包的錯誤實(shí)現(xiàn) Unwrap() error 方法
- 非遞歸:只解包一層,要解包整個錯誤鏈需要循環(huán)調(diào)用
- 與 errors.Is/As 配合:errors.Is 和 errors.As 內(nèi)部會自動處理錯誤鏈
使用場景
- 逐層檢查錯誤鏈通過循環(huán)調(diào)用 errors.Unwrap 遍歷所有嵌套錯誤:
currentErr := err for currentErr != nil { fmt.Println(currentErr) currentErr = errors.Unwrap(currentErr) }
結(jié)合 errors.Is** 和 **errors.As雖然 errors.Is 和 errors.As 會自動遍歷錯誤鏈,但在需要手動提取特定層級錯誤時,可用 Unwrap 配合使用。
自定義錯誤類型的分層處理為自定義錯誤實(shí)現(xiàn) Unwrap() 方法,使其能融入錯誤鏈機(jī)制。
注意事項
僅解包一層每次調(diào)用 errors.Unwrap 只返回直接包裹的下層錯誤。需循環(huán)調(diào)用以遍歷整個鏈。
依賴 Unwrap() 方法只有實(shí)現(xiàn)了 Unwrap() error 方法的錯誤才能被正確解包。例如:
- fmt.Errorf 使用 %w 包裹的錯誤會自動實(shí)現(xiàn)此方法。
- 自定義錯誤需顯式實(shí)現(xiàn) Unwrap() error。
- 空值處理如果 err 為 nil,或未實(shí)現(xiàn) Unwrap,或 Unwrap 返回 nil,則函數(shù)返回 nil。
小結(jié)
errors.Unwrap 是處理錯誤鏈的基礎(chǔ)工具,適用于需要手動逐層解包錯誤的場景。結(jié)合 errors.Is 和 errors.As 可以實(shí)現(xiàn)更高效和安全的錯誤檢查。在實(shí)際開發(fā)中,優(yōu)先使用 errors.Is 和 errors.As 來操作錯誤鏈,僅在需要直接訪問特定層級錯誤時使用 errors.Unwrap。
3.5 Join
簡介
Golang 中的 errors.Join 函數(shù)是 Go 1.20 版本引入的一個錯誤處理工具,用于將多個錯誤合并為一個包裝錯誤(wrapped error)。
它特別適用于需要同時處理多個錯誤(例如并發(fā)操作中多個協(xié)程返回錯誤)的場景。
函數(shù)簽名
func Join(errs ...error) error
- 參數(shù) errs …error:一個可變參數(shù)列表,接收多個 error 類型的值。
- 返回值 如果輸入的 errs 中存在至少一個非 nil 的錯誤,則返回一個合并后的包裝錯誤;否則返回 nil。
核心功能
- 合并多個錯誤errors.Join 會將所有非 nil 的錯誤合并為一個包裝錯誤。合并后的錯誤可以通過 errors.Unwrap 獲取所有原始錯誤的切片。
- 兼容 errors.Is和 errors.As合并后的錯誤支持通過 errors.Is 和 errors.As 檢查或提取其中的特定錯誤。例如:
- errors.Is(err, target):如果合并后的錯誤鏈中存在與 target 匹配的錯誤,返回 true。
- errors.As(err, &target):可以提取合并錯誤鏈中的第一個匹配類型的錯誤。
- 錯誤信息拼接合并后的錯誤信息是多個原始錯誤信息的拼接,以換行符分隔。例如:
err1 := errors.New("error 1") err2 := errors.New("error 2") joinedErr := errors.Join(err1, err2) fmt.Println(joinedErr) // 輸出: // error 1 // error 2 示例代碼 package main import ( "errors" "fmt" "io/fs" "os" ) func main() { err1 := errors.New("file not found") // 創(chuàng)建一個 fs.PathError 錯誤 pathErr := &fs.PathError{ Op: "open", Path: "/etc/passwd", Err: os.ErrPermission, } err2 := fmt.Errorf("operation failed: %w", pathErr) // 合并多個錯誤 joinedErr := errors.Join(err1, err2) // 打印合并后的錯誤信息 fmt.Println("Joined error:") fmt.Println(joinedErr) // 檢查是否包含特定錯誤 if errors.Is(joinedErr, err1) { fmt.Println("Found 'file not found' error") } // 提取錯誤鏈中的某個類型 var targetErr *fs.PathError if errors.As(joinedErr, &targetErr) { fmt.Println("Found PathError:", targetErr) } }
運(yùn)行輸出:
Joined error:
file not found
operation failed: open /etc/passwd: permission denied
Found 'file not found' error
Found PathError: open /etc/passwd: permission denied
使用場景
- 并發(fā)操作中的錯誤收集在多個協(xié)程并發(fā)執(zhí)行時,可以使用 errors.Join 收集所有協(xié)程返回的錯誤:
func processTasks(tasks []Task) error { var wg sync.WaitGroup var mu sync.Mutex var errs []error for _, task := range tasks { wg.Add(1) go func(t Task) { defer wg.Done() if err := t.Run(); err != nil { mu.Lock() errs = append(errs, err) mu.Unlock() } }(task) } wg.Wait() return errors.Join(errs...) }
- 批量操作中的錯誤匯總例如,處理多個文件或請求時,統(tǒng)一返回所有錯誤:
func batchProcess(files []string) error { var errs []error for _, file := range files { if err := processFile(file); err != nil { errs = append(errs, fmt.Errorf("process %s: %w", file, err)) } } return errors.Join(errs...) }
- 兼容已有錯誤處理邏輯合并后的錯誤仍然可以被 errors.Is 和 errors.As 處理,無需修改現(xiàn)有代碼。
注意事項
Go 版本要求errors.Join 僅在 Go 1.20 及以上版本可用。
空參數(shù)處理如果所有輸入錯誤均為 nil,errors.Join 返回 nil。
錯誤解包使用 errors.Unwrap 解包合并后的錯誤時,會返回一個 []error 切片(包含所有非 nil 錯誤)。
錯誤順序合并后的錯誤順序與輸入?yún)?shù)的順序一致,但 errors.Is 和 errors.As 會按順序檢查所有錯誤。
小結(jié)
errors.Join 提供了一種簡潔的方式將多個錯誤合并為一個,特別適用于需要匯總多個錯誤信息的場景(如并發(fā)編程或批量處理)。通過結(jié)合 errors.Is 和 errors.As,可以靈活地檢查或提取合并后的錯誤鏈中的特定錯誤。它是 Go 錯誤處理工具箱中的重要補(bǔ)充,進(jìn)一步提升了錯誤管理的便利性。
4.小結(jié)
errors 包是 Go 語言標(biāo)準(zhǔn)庫中用于錯誤處理的核心包,隨著 Go 版本的演進(jìn),它提供了越來越強(qiáng)大的錯誤處理能力。
以下是主要功能的總結(jié):
- 基礎(chǔ)錯誤創(chuàng)建
- New(text string) error:創(chuàng)建簡單的錯誤對象
- 示例:err := errors.New(“file not found”)
- 錯誤檢查
- Is(err, target error) bool:檢查錯誤鏈中是否包含特定錯誤
- 示例:if errors.Is(err, os.ErrNotExist) {…}
- 錯誤類型提取
- As(err error, target interface{}) bool:從錯誤鏈中提取特定類型的錯誤
- 示例:var perr *fs.PathError; if errors.As(err, &perr) {…}
- 錯誤包裝與解包
- Unwrap(err error) error:解包一層錯誤
- 通過 fmt.Errorf 的 %w 動詞包裝錯誤
- 示例:wrapped := fmt.Errorf(“context: %w”, err)
- 錯誤組合
- Join(errs …error) error(Go 1.20+):合并多個錯誤為一個組合錯誤
- 示例:combined := errors.Join(err1, err2, err3)
errors 包與標(biāo)準(zhǔn)庫中的其他錯誤類型(如 os.PathError、net.OpError 等)配合使用,構(gòu)成了 Go 強(qiáng)大的錯誤處理體系。
參考文獻(xiàn)
到此這篇關(guān)于Golang errors包快速上手的文章就介紹到這了,更多相關(guān)Golang errors包內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang標(biāo)準(zhǔn)庫time時間包的使用
時間和日期是我們編程中經(jīng)常會用到的,本文主要介紹了golang標(biāo)準(zhǔn)庫time時間包的使用,具有一定的參考價值,感興趣的可以了解一下2023-10-10用Go語言標(biāo)準(zhǔn)庫實(shí)現(xiàn)Web服務(wù)之創(chuàng)建路由
在上一節(jié)中創(chuàng)建了項目,這篇文章主要介紹如何用Go語言標(biāo)準(zhǔn)庫創(chuàng)建路由,文中有詳細(xì)的代碼示例,對大家的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以參考下2023-05-05細(xì)細(xì)探究Go 泛型generic設(shè)計
這篇文章主要帶大家細(xì)細(xì)探究了Go 泛型generic設(shè)計及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04Go語言基礎(chǔ)switch條件語句基本用法及示例詳解
這篇文章主要為大家介紹了Go語言基礎(chǔ)switch條件語句基本用法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11go-zero 應(yīng)對海量定時/延遲任務(wù)的技巧
這篇文章主要介紹了go-zero 如何應(yīng)對海量定時/延遲任務(wù),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10