2 回答

TA貢獻1796條經驗 獲得超7個贊
編輯:這個答案早于 Go 1.13,它提供了與所介紹的技術類似的東西。請查看Go 博客:處理 Go 1.13 中的錯誤。
您應該處理錯誤,或者不處理錯誤,而是將其委托給更高級別(給調用者)。處理錯誤并返回它是不好的做法,就好像調用者也這樣做一樣,錯誤可能會被處理多次。
處理錯誤意味著檢查它并根據它做出決定,這可能是您簡單地記錄它,但這也算作“處理”它。
如果您選擇不處理而是將其委托給更高級別,那可能很好,但不要只返回您得到的錯誤值,因為它可能對沒有上下文的調用者毫無意義。
注釋錯誤
一個非常好的和推薦的委派方式是Annotating errors。這意味著您創建并返回一個新的錯誤值,但舊的錯誤值也包含在返回值中。包裝器為包裝的錯誤提供上下文。
沒有為標注錯誤公共圖書館:github.com/pkg/errors; 及其godoc:errors
它基本上有 2 個功能: 1 用于包裝現有錯誤:
func Wrap(cause error, message string) error
還有一個用于提取包裝錯誤:
func Cause(err error) error
使用這些,您的錯誤處理可能如下所示:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return errors.New("Object1 illegal state: value is 0")
}
return nil
}
第二級:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return errors.Wrap(err, "Object2 illegal state: Object1 is invalid")
}
return nil
}
第三級:僅調用第二級檢查:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return errors.Wrap(err, "Object3 illegal state: Object2 is invalid")
}
return nil
}
請注意,由于這些CheckXX()方法不處理錯誤,因此它們不會記錄任何內容。他們正在委派帶注釋的錯誤。
如果有人使用ObjectThreeHiggerLevel決定處理錯誤:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
將呈現以下漂亮的輸出:
Object3 illegal state: Object2 is invalid: Object2 illegal state: Object1 is invalid: Object1 illegal state: value is 0
沒有多個日志的污染,并且所有細節和上下文都被保留了,因為我們使用errors.Wrap()它產生一個錯誤值,該錯誤值格式化為 a string,它遞歸地保留包裝的錯誤:錯誤堆棧。
您可以在博客文章中閱讀有關此技術的更多信息:
Dave Cheney:不要只檢查錯誤,要優雅地處理它們
“擴展”錯誤
如果您喜歡更簡單的事情和/或您不想與外部庫發生麻煩并且您無法提取原始錯誤(確切的錯誤值,而不是您可以的錯誤字符串),那么您可以只需使用上下文擴展錯誤并返回這個新的擴展錯誤。
擴展錯誤最容易通過使用fmt.Errorf()它來完成,它允許您創建一個“漂亮”的格式化錯誤消息,它會返回一個類型的值,error因此您可以直接返回它。
使用fmt.Errorf(),您的錯誤處理可能如下所示:
func (o *ObjectOne) CheckValue() error {
if o.someValue == 0 {
return fmt.Errorf("Object1 illegal state: value is %d", o.someValue)
}
return nil
}
第二級:
func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {
if err := oT.objectOne.CheckValue(); err != nil {
return fmt.Errorf("Object2 illegal state: %v", err)
}
return nil
}
第三級:僅調用第二級檢查:
func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {
if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {
return fmt.Errorf("Object3 illegal state: %v", err)
}
return nil
}
ObjectThreeHiggerLevel如果它決定“處理”它,將顯示以下錯誤消息:
o3 := &ObjectThreeHiggerLevel{}
if err := o3.CheckObjectTwoIsReady(); err != nil {
fmt.Println(err)
}
將呈現以下漂亮的輸出:
Object3 illegal state: Object2 illegal state: Object1 illegal state: value is 0
請務必閱讀博文:錯誤處理和 Go

TA貢獻1801條經驗 獲得超16個贊
有各種庫將堆棧跟蹤嵌入到 Go 錯誤中。只需使用其中之一創建錯誤,它就會冒出完整的堆棧上下文,您可以稍后檢查或記錄。
一個這樣的圖書館:
https://github.com/go-errors/errors
還有一些我忘記了。
- 2 回答
- 0 關注
- 222 瀏覽
添加回答
舉報