错误处理
Go 语言的错误处理采用显式错误返回的方式,这是 Go 语言设计哲学的重要体现。
错误类型
error 接口
Go 语言中的错误是一个接口:
go
type error interface {
Error() string
}创建错误
go
import "errors"
// 方式 1: 使用 errors.New
err := errors.New("发生了错误")
// 方式 2: 使用 fmt.Errorf
err := fmt.Errorf("用户 %s 不存在", username)
// 方式 3: 自定义错误类型
type MyError struct {
Code int
Message string
}
func (e *MyError) Error() string {
return fmt.Sprintf("错误代码: %d, 消息: %s", e.Code, e.Message)
}
err := &MyError{Code: 404, Message: "未找到"}错误处理模式
基本模式
go
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("除数不能为0")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println("结果:", result)
}错误检查
go
result, err := doSomething()
if err != nil {
// 处理错误
log.Printf("错误: %v", err)
return err
}忽略错误(不推荐)
go
result, _ := doSomething() // 忽略错误错误包装
fmt.Errorf 和 %w
Go 1.13+ 引入了错误包装:
go
func readFile(filename string) error {
data, err := ioutil.ReadFile(filename)
if err != nil {
return fmt.Errorf("读取文件失败: %w", err)
}
// 处理数据
return nil
}errors.Unwrap
go
err := readFile("test.txt")
if err != nil {
unwrapped := errors.Unwrap(err)
fmt.Println("原始错误:", unwrapped)
}errors.Is
检查错误链中是否包含特定错误:
go
if errors.Is(err, os.ErrNotExist) {
fmt.Println("文件不存在")
}errors.As
将错误转换为特定类型:
go
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("路径错误:", pathErr.Path)
}自定义错误
简单自定义错误
go
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("字段 %s: %s", e.Field, e.Message)
}
func validateUser(username string) error {
if username == "" {
return &ValidationError{
Field: "username",
Message: "用户名不能为空",
}
}
return nil
}带错误码的错误
go
type AppError struct {
Code int
Message string
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("错误代码 %d: %s (%v)", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("错误代码 %d: %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error {
return e.Err
}Panic 和 Recover
Panic
panic 用于处理不可恢复的错误:
go
func mustDivide(a, b float64) float64 {
if b == 0 {
panic("除数不能为0")
}
return a / b
}Recover
recover 用于捕获 panic:
go
func safeDivide(a, b float64) (result float64, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("发生 panic: %v", r)
}
}()
result = mustDivide(a, b)
return
}使用场景
- Panic: 用于不可恢复的错误,如程序逻辑错误
- Error: 用于可预期的错误,如文件不存在、网络错误等
错误处理最佳实践
1. 尽早返回错误
go
// 好的做法
func process(data []byte) error {
if len(data) == 0 {
return errors.New("数据为空")
}
// 继续处理
return nil
}
// 不好的做法
func process(data []byte) error {
if len(data) != 0 {
// 大量嵌套代码
} else {
return errors.New("数据为空")
}
return nil
}2. 提供上下文信息
go
func readConfig(filename string) (*Config, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("打开配置文件失败 %s: %w", filename, err)
}
defer file.Close()
// 读取配置
return config, nil
}3. 使用 sentinel 错误
go
var (
ErrNotFound = errors.New("未找到")
ErrInvalidData = errors.New("无效数据")
)
func findUser(id int) (*User, error) {
if id <= 0 {
return nil, ErrInvalidData
}
// 查找用户
if user == nil {
return nil, ErrNotFound
}
return user, nil
}4. 错误日志记录
go
import "log"
func handleRequest() error {
err := doSomething()
if err != nil {
log.Printf("处理请求失败: %v", err)
return err
}
return nil
}完整示例
go
package main
import (
"errors"
"fmt"
"log"
)
// 自定义错误
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证失败 - %s: %s", e.Field, e.Message)
}
// 带错误码的错误
type AppError struct {
Code int
Message string
Err error
}
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("[%d] %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("[%d] %s", e.Code, e.Message)
}
func (e *AppError) Unwrap() error {
return e.Err
}
// 业务函数
func validateUser(username, email string) error {
if username == "" {
return &ValidationError{
Field: "username",
Message: "用户名不能为空",
}
}
if email == "" {
return &ValidationError{
Field: "email",
Message: "邮箱不能为空",
}
}
return nil
}
func createUser(username, email string) error {
if err := validateUser(username, email); err != nil {
return &AppError{
Code: 400,
Message: "创建用户失败",
Err: err,
}
}
// 创建用户逻辑
return nil
}
func main() {
// 错误处理示例
err := createUser("", "test@example.com")
if err != nil {
log.Printf("错误: %v", err)
// 检查错误类型
var validationErr *ValidationError
if errors.As(err, &validationErr) {
fmt.Printf("验证错误: %s\n", validationErr.Field)
}
}
// Panic 和 Recover 示例
defer func() {
if r := recover(); r != nil {
fmt.Printf("捕获到 panic: %v\n", r)
}
}()
panic("这是一个 panic 示例")
}下一步
接下来我们将学习:
