Go-错误处理模式(error 接口与 errors 包)

1. error 接口的定义

在 Go 中,错误通过 error 接口表示,该接口只包含一个方法 Error(),返回错误的描述信息。

1
2
3
type error interface {
Error() string
}

任何实现了 Error 方法的类型都被视为 error 类型,允许返回详细的错误信息。


2. errors 包的基本用法

Go 提供了 errors 包来帮助我们创建简单的错误,errors.New 是创建错误的最基本方式。

示例代码

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"errors"
"fmt"
)

func main() {
err := errors.New("an error occurred")
fmt.Println(err)
}

3. 自定义错误

在实际开发中,我们通常需要创建自定义错误类型来提供更多上下文信息。自定义错误类型可以通过实现 error 接口来实现。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
)

type MyError struct {
Code int
Message string
}

func (e *MyError) Error() string {
return fmt.Sprintf("Code %d: %s", e.Code, e.Message)
}

func main() {
err := &MyError{Code: 404, Message: "Resource not found"}
fmt.Println(err)
}

4. 错误包装与解包

Go 1.13 之后引入了 fmt.Errorferrors 包中的 UnwrapIsAs 函数,用于错误的包装与解包。

错误包装

使用 fmt.Errorf 包装错误信息,添加更多上下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"errors"
"fmt"
)

func someFunc() error {
return errors.New("initial error")
}

func main() {
err := someFunc()
wrappedErr := fmt.Errorf("failed to execute someFunc: %w", err)
fmt.Println(wrappedErr)
}

错误解包与检查

  • errors.Is 用于判断错误是否是特定的错误类型。
  • errors.As 用于将错误转换为特定的类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"errors"
"fmt"
)

var ErrNotFound = errors.New("not found")

func main() {
err := fmt.Errorf("wrapper: %w", ErrNotFound)

if errors.Is(err, ErrNotFound) {
fmt.Println("The error is ErrNotFound")
}
}

5. 错误检查模式与最佳实践

1. 检查每个返回的错误

Go 的惯例是检查每个可能返回错误的函数。例如:

1
2
3
if err != nil {
return fmt.Errorf("operation failed: %w", err)
}

2. 提供上下文

在返回错误时,通过包装提供额外的上下文:

1
return fmt.Errorf("unable to connect to server: %w", err)

3. 使用特定错误类型

自定义错误类型和 errors.Iserrors.As 可以帮助更精细的错误判断和处理。


总结

  • error 接口 是 Go 错误处理的核心。
  • 使用 errors 包fmt.Errorf 包装错误,提供上下文。
  • errors.Iserrors.As 帮助更好地检查和处理错误类型。