异常处理
2026年9月6日
Go 没有 try/catch。能恢复的失败用 error 当返回值,调用方必须看一眼。panic 留给「不该发生」的情况。
n, err := fmt.Println("ok")
if err != nil {
return err
}
_ = n
go
常见的异常处理
向上抛
自己决定不了怎么处理,就原样或包一层返回给调用方。库、框架里很常见。
func readConfig(path string) ([]byte, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("读配置 %s: %w", path, err)
}
return b, nil
}
go
%w 能保住原来的错误,上面再用 errors.Is / errors.As 判断。
中断程序
初始化阶段失败,后面跑下去也没意义,直接停:
func main() {
cfg, err := readConfig("config.json")
if err != nil {
log.Fatal(err)
}
_ = cfg
}
go
log.Fatal、os.Exit(1)、panic(err) 都会结束进程。服务启动读配置、连不上必须有的数据库,用这种。
恢复程序
panic 会沿着调用栈往上走,直到进程退出。某个函数里用 defer + recover 可以接住,让程序继续:
func safe(fn func()) {
defer func() {
if r := recover(); r != nil {
fmt.Println("接住了:", r)
debug.PrintStack()
}
}()
fn()
}
go
recover 只在 defer 里有用。可以放在调用链的任何一层,通常放在最外层(HTTP 中间件、worker 循环),避免一个请求把整个进程打死。
业务错误优先返回 error。数组越界、往关了的 channel 发送,运行时自己会 panic,外层 recover 只当最后一道门。