Go语言的错误处理

Go 语言没有像 try/catch 这样的异常捕获机制,它的错误处理核心是:将错误作为返回值显式返回,由调用方自行决定如何处理。这种设计让错误处理更清晰、更可控,也符合 Go 语言 "简洁、显式" 的设计哲学。

一、基础:error 接口

Go 语言的错误处理基石是内置的 error 接口,它的定义非常简单:

Go 复制代码
type error interface {
    Error() string
}

任何实现了 Error() string 方法的类型,都可以作为错误类型返回。

1. 最基础的错误使用方式

Go 复制代码
package main

import (
    "errors"
    "fmt"
)

// 定义一个可能出错的函数:除法运算,除数不能为0
func divide(a, b int) (int, error) {
    if b == 0 {
        // 使用errors.New创建基础错误
        return 0, errors.New("除数不能为0")
    }
    // 无错误时,返回结果+nil(表示无错误)
    return a / b, nil
}

func main() {
    // 调用函数,接收结果和错误
    res, err := divide(10, 0)
    // 核心:先检查错误,再使用结果
    if err != nil {
        fmt.Printf("运算失败:%v\n", err) // 输出:运算失败:除数不能为0
    } else {
        fmt.Printf("运算结果:%d\n", res)
    }

    // 正常情况
    res2, err2 := divide(10, 2)
    if err2 != nil {
        fmt.Printf("运算失败:%v\n", err2)
    } else {
        fmt.Printf("运算结果:%d\n", res2) // 输出:运算结果:5
    }
}

2. 自定义错误类型

当需要携带更多错误信息(如错误码、上下文)时,可以自定义实现 error 接口的类型:

Go 复制代码
package main

import (
    "fmt"
)

// 自定义错误类型,包含错误码和错误信息
type MyError struct {
    Code int    // 错误码
    Msg  string // 错误信息
}

// 实现error接口的Error()方法
func (e *MyError) Error() string {
    return fmt.Sprintf("错误码:%d,信息:%s", e.Code, e.Msg)
}

// 模拟业务函数返回自定义错误
func getUserInfo(id int) (string, error) {
    if id <= 0 {
        return "", &MyError{Code: 400, Msg: "用户ID不合法"}
    }
    return fmt.Sprintf("用户%d的信息", id), nil
}

func main() {
    info, err := getUserInfo(-1)
    if err != nil {
        // 类型断言,获取自定义错误的详细信息
        if myErr, ok := err.(*MyError); ok {
            fmt.Printf("业务错误:码=%d,信息=%s\n", myErr.Code, myErr.Msg)
        } else {
            fmt.Printf("未知错误:%v\n", err)
        }
        return
    }
    fmt.Println(info)
}

3. fmt.Errorf:带格式化信息的错误

Go 1.13+ 新增了 %w 占位符,支持错误包装(Error Wrapping),可以保留原始错误信息:

Go 复制代码
package main

import (
    "errors"
    "fmt"
)

func readFile() error {
    // 模拟底层错误
    baseErr := errors.New("文件不存在")
    // 包装错误,添加上下文信息
    return fmt.Errorf("读取文件失败:%w", baseErr)
}

func main() {
    err := readFile()
    if err != nil {
        fmt.Println("错误信息:", err) // 输出:错误信息:读取文件失败:文件不存在
        
        // 使用errors.Is检查原始错误
        if errors.Is(err, errors.New("文件不存在")) {
            fmt.Println("确认:底层错误是文件不存在")
        }

        // 模拟另一个自定义错误
        customErr := &MyError{Code: 500, Msg: "服务器错误"}
        wrappedErr := fmt.Errorf("处理失败:%w", customErr)
        // 使用errors.As提取包装的自定义错误
        var targetErr *MyError
        if errors.As(wrappedErr, &targetErr) {
            fmt.Printf("提取到自定义错误:码=%d,信息=%s\n", targetErr.Code, targetErr.Msg)
        }
    }
}

// 复用前面定义的MyError类型
type MyError struct {
    Code int
    Msg  string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("错误码:%d,信息:%s", e.Code, e.Msg)
}

4. panic/recover:处理极端错误

Go 语言中 panic 用于表示不可恢复的严重错误 (如数组越界、空指针),recover 用于捕获 panic 并恢复程序(仅在 defer 函数中有效),这不是常规的错误处理方式,仅用于兜底:

Go 复制代码
package main

import "fmt"

func riskyFunc() {
    defer func() {
        // 捕获panic,恢复程序
        if r := recover(); r != nil {
            fmt.Printf("捕获到panic:%v\n", r) // 输出:捕获到panic:运行时错误:除数为0
        }
    }()

    // 触发panic
    fmt.Println(10 / 0)
}

func main() {
    riskyFunc()
    fmt.Println("程序继续执行") // 能正常输出,说明程序被恢复
}

二、错误处理的最佳实践

  1. 优先返回错误,而非 panic :只有程序无法继续运行的极端情况才用 panic(如初始化失败)。
  2. 错误检查要及时:调用函数后,先检查错误,再使用返回值,这是 Go 的编码惯例。
  3. 错误信息要具体:包含上下文(如文件名、参数值),方便定位问题,避免模糊的 "操作失败"。
  4. 合理包装错误 :使用 fmt.Errorf("%w") 保留原始错误,便于上层调用方排查根因。
  5. 避免重复处理错误不要在多层调用中重复打印错误,由最外层统一处理即可

总结

  1. Go 语言错误处理的核心是将错误作为返回值显式返回 ,依赖内置的 error 接口,无 try/catch 机制。
  2. 基础错误用 errors.New 创建,带格式化信息用 fmt.Errorf,Go 1.13+ 支持 %w 包装错误,通过 errors.Is/As 解析。
  3. panic/recover 仅用于处理极端错误,常规业务逻辑应优先使用返回错误的方式,且遵循 "先检查错误,再使用结果" 的惯例。
相关推荐
yugi9878382 小时前
MATLAB的多层感知器(MLP)与极限学习机(ELM)实现
开发语言·matlab
Never_Satisfied3 小时前
C#获取汉字拼音字母方法总结
开发语言·c#
zh_xuan3 小时前
kotlin 密封类
开发语言·kotlin
码小猿的CPP工坊3 小时前
C++软件开发之内存泄漏闭坑方法
开发语言·c++
Ethan-D3 小时前
#每日一题19 回溯 + 全排列思想
java·开发语言·python·算法·leetcode
满栀5853 小时前
分页插件制作
开发语言·前端·javascript·jquery
froginwe113 小时前
C 标准库 - <stdio.h>
开发语言
zwtahql3 小时前
php源码级别调试
开发语言·php
qq_406176143 小时前
深入剖析JavaScript原型与原型链:从底层机制到实战应用
开发语言·前端·javascript·原型模式