跳转到内容

Go 错误处理

来自代码酷

Go错误处理[编辑 | 编辑源代码]

Go语言采用显式错误处理机制,强调开发者主动处理可能发生的错误情况。这种设计哲学与传统的异常处理机制(如try-catch)有本质区别,是Go控制流中的重要组成部分。

基本概念[编辑 | 编辑源代码]

在Go中,错误(error)是一个内置接口类型:

type error interface {
    Error() string
}

错误处理的典型模式是通过多返回值机制,将错误作为函数的最后一个返回值:

result, err := someFunction()
if err != nil {
    // 处理错误
}
// 继续正常流程

错误创建[编辑 | 编辑源代码]

标准库提供了两种主要错误创建方式:

1. 使用errors.New

err := errors.New("文件未找到")

2. 使用fmt.Errorf(支持格式化):

err := fmt.Errorf("无效的用户ID: %d", userID)

错误处理模式[编辑 | 编辑源代码]

基本错误检查[编辑 | 编辑源代码]

最常用的模式是立即检查错误:

file, err := os.Open("config.json")
if err != nil {
    log.Fatal("无法打开配置文件:", err)
}
defer file.Close()

错误类型断言[编辑 | 编辑源代码]

当需要区分不同类型的错误时:

if err != nil {
    if os.IsNotExist(err) {
        fmt.Println("文件不存在")
    } else if os.IsPermission(err) {
        fmt.Println("权限不足")
    } else {
        fmt.Println("未知错误:", err)
    }
}

自定义错误类型[编辑 | 编辑源代码]

定义结构体实现error接口:

type APIError struct {
    StatusCode int
    Message    string
}

func (e *APIError) Error() string {
    return fmt.Sprintf("API错误[%d]: %s", e.StatusCode, e.Message)
}

// 使用示例
func GetUser(id int) (*User, error) {
    if id < 1 {
        return nil, &APIError{400, "无效的用户ID"}
    }
    // ...
}

高级错误处理[编辑 | 编辑源代码]

错误包装[编辑 | 编辑源代码]

Go 1.13引入了错误包装机制:

if err != nil {
    return fmt.Errorf("处理用户数据失败: %w", err)
}

使用errors.Iserrors.As进行检查:

var apiErr *APIError
if errors.As(err, &apiErr) {
    fmt.Println("捕获到API错误:", apiErr.StatusCode)
}

panic和recover[编辑 | 编辑源代码]

用于处理不可恢复的错误:

func safeCall() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("恢复panic:", r)
        }
    }()
    panic("发生严重错误")
}

最佳实践[编辑 | 编辑源代码]

1. 始终检查错误:不要忽略函数返回的错误 2. 提供有意义的错误信息:帮助调试和问题定位 3. 避免过度使用panic:仅在真正不可恢复的情况下使用 4. 考虑错误上下文:使用错误包装保留原始错误 5. 文档化可能返回的错误:让调用者知道需要处理哪些错误

实际案例[编辑 | 编辑源代码]

文件处理示例[编辑 | 编辑源代码]

func ReadConfig(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("打开配置文件失败: %w", err)
    }
    defer file.Close()

    data, err := io.ReadAll(file)
    if err != nil {
        return nil, fmt.Errorf("读取配置文件失败: %w", err)
    }

    return data, nil
}

HTTP服务错误处理[编辑 | 编辑源代码]

func handleRequest(w http.ResponseWriter, r *http.Request) {
    user, err := getUserFromDB(r.FormValue("id"))
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            http.Error(w, "用户不存在", http.StatusNotFound)
        } else {
            http.Error(w, "服务器错误", http.StatusInternalServerError)
            log.Printf("处理请求失败: %v", err)
        }
        return
    }
    // 处理正常情况...
}

错误处理流程图[编辑 | 编辑源代码]

graph TD A[调用函数] --> B{是否有错误?} B -->|是| C[处理错误] B -->|否| D[继续正常流程] C --> E[记录日志/返回错误/恢复] E --> F[结束或重试] D --> G[执行后续操作]

数学表达[编辑 | 编辑源代码]

错误处理可以被视为程序控制流的分支决策。设P为程序状态,E为错误条件,则:

P={ferror(P)如果 E(P) 为真fnormal(P)否则

其中ferrorfnormal分别代表错误处理和正常执行的函数。