问题的背景:
在Go 1.13之前,错误只是一个简单的接口。为了提供额外的错误上下文,人们经常创建自己的类型,但没有像现代Java或C#那样的结构化嵌套机制。随着Go 1.13的发布,引入了一种标准的错误包装方式,通过fmt.Errorf函数和根本原因的识别(errors.Is/errors.As)。
问题:
在复杂应用程序中,错误可能在堆栈的不同层次上发生,重要的是在从代码深处返回错误时不要丢失上下文。否则,调试将变得困难,无法了解链中的故障发生在哪里。
解决方案:
Go建议将错误包装在新的错误对象中,以包含原因。为此,在fmt.Errorf中使用%w,而要检查深层原因,使用errors.Is或errors.As来自errors包。
代码示例:
import ( "errors" "fmt" ) var ErrNotFound = errors.New("未找到") func getData() error { return fmt.Errorf("服务数据库: %w", ErrNotFound) } func main() { err := getData() if errors.Is(err, ErrNotFound) { fmt.Println("检测到原因: 未找到") } else { fmt.Println("其他错误:", err) } }
关键特点:
%w通过fmt.Errorf嵌套错误。errors.Is和errors.As识别嵌套的错误原因。用于通过fmt.Errorf包装错误的格式化操作符是什么?
正确的操作符是%w,而不是%v — 只有%w支持解包。
代码示例:
fmt.Errorf("错误: %w", err)
可以在不使用fmt.Errorf的情况下手动创建错误链,并通过errors.Is识别它们吗?
不可以,必须实现Unwrap接口,否则标准函数无法解包链。
接口代码示例:
type wrappedError struct { msg string err error } func (w wrappedError) Error() string { return w.msg + ": " + w.err.Error() } func (w wrappedError) Unwrap() error { return w.err }
如果嵌套错误是nil,包装错误是否返回nil值?
不,如果嵌套错误是nil,则包装错误只有在正确使用的情况下才会返回nil。但是,如果手动创建一个包装结构,错误字段为nil,则不会是nil。这通常在检查时导致困惑。
%w而仅使用%v — 失去了分析链的能力。Unwrap()。errors.Is/As进行检查,而只直接比较错误。在应用程序中,只是在每个级别返回新错误,文本为:
return errors.New("数据库写入错误")
优点:
缺点:
使用%w包装错误并通过errors.Is进行分析:
if errors.Is(err, ErrNotFound) { return fmt.Errorf("服务层错误: %w", err) }
优点:
缺点: