错误包装 #
一、错误包装概述 #
Go 1.13引入了错误包装机制,允许错误包含其他错误。
二、fmt.Errorf包装 #
2.1 %w动词 #
go
err := fmt.Errorf("operation failed: %w", originalErr)
2.2 示例 #
go
func readFile(path string) error {
_, err := os.Open(path)
if err != nil {
return fmt.Errorf("open file %s: %w", path, err)
}
return nil
}
三、errors.Is #
3.1 检查错误值 #
go
if errors.Is(err, os.ErrNotExist) {
fmt.Println("File not found")
}
3.2 自定义错误 #
go
var ErrNotFound = errors.New("not found")
func find(id int) error {
return fmt.Errorf("find %d: %w", id, ErrNotFound)
}
func main() {
err := find(1)
if errors.Is(err, ErrNotFound) {
fmt.Println("Not found")
}
}
3.3 错误链 #
go
err1 := errors.New("error 1")
err2 := fmt.Errorf("error 2: %w", err1)
err3 := fmt.Errorf("error 3: %w", err2)
fmt.Println(errors.Is(err3, err1)) // true
四、errors.As #
4.1 类型检查 #
go
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("Path:", pathErr.Path)
}
4.2 自定义类型 #
go
type ValidationError struct {
Field string
}
func (e *ValidationError) Error() string {
return "validation error: " + e.Field
}
func validate() error {
return &ValidationError{Field: "name"}
}
func main() {
err := validate()
var ve *ValidationError
if errors.As(err, &ve) {
fmt.Println("Field:", ve.Field)
}
}
4.3 错误链中的类型 #
go
err1 := &ValidationError{Field: "name"}
err2 := fmt.Errorf("validate: %w", err1)
var ve *ValidationError
if errors.As(err2, &ve) {
fmt.Println("Field:", ve.Field) // name
}
五、Unwrap #
5.1 errors.Unwrap #
go
err1 := errors.New("error 1")
err2 := fmt.Errorf("error 2: %w", err1)
unwrapped := errors.Unwrap(err2)
fmt.Println(unwrapped) // error 1
5.2 自定义Unwrap #
go
type WrappedError struct {
Msg string
Err error
}
func (e *WrappedError) Error() string {
return e.Msg
}
func (e *WrappedError) Unwrap() error {
return e.Err
}
六、实际应用 #
6.1 分层错误处理 #
go
func service() error {
if err := repository(); err != nil {
return fmt.Errorf("service: %w", err)
}
return nil
}
func repository() error {
if err := db(); err != nil {
return fmt.Errorf("repository: %w", err)
}
return nil
}
func main() {
err := service()
var dbErr *DBError
if errors.As(err, &dbErr) {
// 处理数据库错误
}
}
6.2 错误判断 #
go
func handleError(err error) {
if errors.Is(err, sql.ErrNoRows) {
fmt.Println("No data found")
} else if errors.Is(err, context.Canceled) {
fmt.Println("Operation canceled")
} else {
fmt.Println("Unknown error:", err)
}
}
6.3 错误恢复 #
go
func retryable(err error) bool {
var netErr net.Error
if errors.As(err, &netErr) {
return netErr.Timeout() || netErr.Temporary()
}
return false
}
七、最佳实践 #
7.1 使用%w包装 #
go
return fmt.Errorf("operation failed: %w", err)
7.2 使用errors.Is检查 #
go
if errors.Is(err, ErrNotFound) {
// 处理
}
7.3 使用errors.As提取 #
go
var ve *ValidationError
if errors.As(err, &ve) {
// 使用ve
}
八、总结 #
错误包装要点:
| 函数 | 说明 |
|---|---|
| fmt.Errorf(“%w”) | 包装错误 |
| errors.Is | 检查错误值 |
| errors.As | 检查错误类型 |
| errors.Unwrap | 解包错误 |
关键点:
准备好学习panic与recover了吗?让我们进入下一章!
最后更新:2026-03-26