异常处理

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.Fatalos.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 只当最后一道门。


参考文档