全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-13 7 分钟 ✍️ juanwangdev

Go错误处理与接口设计

Go错误基于接口设计,灵活且可扩展。

error接口定义

标准error接口

Go
// 标准库定义
type error interface {
    Error() string
}

// 任何实现Error()方法的类型都是error
type MyError struct {
    Msg string
}

func (e MyError) Error() string {
    return e.Msg
}

var err error = MyError{Msg: "出错了"}
fmt.Println(err.Error())

自定义错误类型

结构体错误

Go
type ValidationError struct {
    Field string
    Value interface{}
    Msg   string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("验证失败: %s=%v, %s", e.Field, e.Value, e.Msg)
}

// 使用
func validate(name string) error {
    if name == "" {
        return ValidationError{
            Field: "name",
            Value: name,
            Msg:   "不能为空",
        }
    }
    return nil
}

包装错误(Go 1.13+)

Go
import "errors"

// 创建错误
err := errors.New("基础错误")

// 包装错误
wrapped := fmt.Errorf("操作失败: %w", err)

// 获取原始错误
original := errors.Unwrap(wrapped)

错误检查

errors.Is检查类型

Go
var ErrNotFound = errors.New("未找到")

func find(id int) error {
    return ErrNotFound
}

func main() {
    err := find(1)
    if errors.Is(err, ErrNotFound) {
        fmt.Println("记录不存在")
    }
}

errors.As检查类型并提取

Go
type TimeoutError struct {
    Duration time.Duration
}

func (e TimeoutError) Error() string {
    return fmt.Sprintf("超时: %v", e.Duration)
}

func main() {
    err := TimeoutError{Duration: 5 * time.Second}

    var te TimeoutError
    if errors.As(err, &te) {
        fmt.Println("超时时间:", te.Duration)
    }
}

接口设计中的错误处理

返回错误而非panic

Go
// 不推荐:panic
func createUser(name string) User {
    if name == "" {
        panic("名字不能为空")
    }
    return User{Name: name}
}

// 推荐:返回错误
func createUser(name string) (User, error) {
    if name == "" {
        return User{}, ValidationError{Field: "name", Msg: "不能为空"}
    }
    return User{Name: name}, nil
}

错误作为接口返回

Go
type Repository interface {
    Find(id int) (*User, error)
    Save(user *User) error
    Delete(id int) error
}

// 实现者返回具体错误类型
type MySQLRepo struct{}

func (r MySQLRepo) Find(id int) (*User, error) {
    // 返回具体错误
    return nil, ErrNotFound
}

错误处理模式

检查并处理

Go
func process() error {
    user, err := repo.Find(1)
    if err != nil {
        if errors.Is(err, ErrNotFound) {
            // 特定错误处理
            return fmt.Errorf("用户不存在: %w", err)
        }
        // 其他错误
        return fmt.Errorf("查找失败: %w", err)
    }

    err = repo.Save(user)
    if err != nil {
        return fmt.Errorf("保存失败: %w", err)
    }

    return nil
}

多层错误包装

Go
func service() error {
    if err := repo.Find(1); err != nil {
        return fmt.Errorf("服务层: %w", err)
    }
    return nil
}

func handler() error {
    if err := service(); err != nil {
        return fmt.Errorf("处理器层: %w", err)
    }
    return nil
}

// 最终错误包含完整调用链
// 处理器层: 服务层: 用户不存在: 未找到

错误类型设计对比

方式用途示例
errors.New简单错误errors.New("失败")
fmt.Errorf格式化错误fmt.Errorf("失败: %d", id)
自定义结构体携带上下文ValidationError{Field:"name"}
%w包装保留调用链fmt.Errorf("失败: %w", err)
errors.Is检查类型errors.Is(err, ErrNotFound)
errors.As提取信息errors.As(err, &te)

要点总结

  • error接口只需实现Error()方法
  • 自定义错误类型可携带更多上下文
  • 使用%w包装错误保留调用链
  • errors.Is检查特定错误类型
  • errors.As提取自定义错误信息
  • 接口返回error而非panic
  • 每层包装错误增加上下文
  • 错误处理是接口设计的重要部分

📝 发现内容有误?点击此处直接编辑

← 上一篇 Go版本管理与向后兼容性
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库