错误处理
基本错误处理
Go语言中的错误处理机制。
package main
import (
"errors"
"fmt"
)
// 自定义错误
var (
ErrNotFound = errors.New("记录未找到")
ErrInvalidInput = errors.New("无效的输入")
)
// 返回错误的函数
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
func main() {
// 基本错误处理
result, err := divide(10, 0)
if err != nil {
fmt.Printf("错误: %v\n", err)
return
}
fmt.Printf("结果: %v\n", result)
// 错误类型判断
if err == ErrNotFound {
fmt.Println("处理未找到错误")
} else if err == ErrInvalidInput {
fmt.Println("处理无效输入错误")
}
}
错误包装
使用fmt.Errorf和errors包包装错误。
package main
import (
"errors"
"fmt"
)
// 使用fmt.Errorf包装错误
func processUser(id int) error {
if id <= 0 {
return fmt.Errorf("处理用户失败: %w", ErrInvalidInput)
}
return nil
}
// 使用errors包包装错误
func validateUser(id int) error {
if id <= 0 {
return errors.Wrap(ErrInvalidInput, "验证用户失败")
}
return nil
}
func main() {
// 错误包装示例
err := processUser(-1)
if err != nil {
fmt.Printf("错误: %v\n", err)
// 检查原始错误
if errors.Is(err, ErrInvalidInput) {
fmt.Println("是无效输入错误")
}
}
// 错误解包
err = validateUser(-1)
if err != nil {
fmt.Printf("错误: %v\n", err)
// 获取原始错误
if errors.Is(err, ErrInvalidInput) {
fmt.Println("是无效输入错误")
}
}
}
自定义错误类型
创建自定义错误类型。
package main
import (
"fmt"
"time"
)
// 自定义错误类型
type ValidationError struct {
Field string
Message string
Time time.Time
}
// 实现error接口
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证错误 [%s]: %s (时间: %v)",
e.Field, e.Message, e.Time.Format(time.RFC3339))
}
// 创建验证错误
func NewValidationError(field, message string) error {
return &ValidationError{
Field: field,
Message: message,
Time: time.Now(),
}
}
func main() {
// 使用自定义错误
err := NewValidationError("email", "无效的邮箱格式")
if err != nil {
fmt.Printf("错误: %v\n", err)
// 类型断言
if ve, ok := err.(*ValidationError); ok {
fmt.Printf("字段: %s\n", ve.Field)
fmt.Printf("消息: %s\n", ve.Message)
fmt.Printf("时间: %v\n", ve.Time)
}
}
}
错误处理最佳实践
错误处理的最佳实践和注意事项。
# 1. 错误检查
- 始终检查错误
- 不要忽略错误
- 在适当的地方处理错误
# 2. 错误信息
- 提供有意义的错误信息
- 包含上下文信息
- 使用清晰的错误描述
# 3. 错误传播
- 在适当的层级处理错误
- 不要过度包装错误
- 保持错误链的简洁
# 4. 错误类型
- 使用自定义错误类型
- 实现error接口
- 提供额外的错误信息
# 5. 错误处理模式
- 使用defer处理清理操作
- 使用panic/recover处理严重错误
- 使用日志记录错误
# 示例
func processFile(filename string) error {
// 打开文件
file, err := os.Open(filename)
if err != nil {
return fmt.Errorf("打开文件失败: %w", err)
}
defer file.Close()
// 处理文件
if err := processContent(file); err != nil {
return fmt.Errorf("处理文件内容失败: %w", err)
}
return nil
}
错误处理工具
常用的错误处理工具和库。
# 1. errors包
import "github.com/pkg/errors"
// 包装错误
err = errors.Wrap(err, "处理失败")
// 检查错误
if errors.Is(err, ErrNotFound) {
// 处理错误
}
// 获取原始错误
originalErr := errors.Cause(err)
# 2. log包
import "log"
// 记录错误
log.Printf("错误: %v", err)
log.Fatal(err) // 记录错误并退出
# 3. 自定义错误工具
type ErrorCode int
const (
ErrCodeNotFound ErrorCode = iota + 1
ErrCodeInvalidInput
ErrCodeInternal
)
type AppError struct {
Code ErrorCode
Message string
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %v", e.Message, e.Err)
}
return e.Message
}