主函数退出方式 (Exit)
约 807 字大约 3 分钟
2025-09-04
Go 程序使用 os.Exit 或者 log.Fatal* 立即退出 (使用panic不是退出程序的好方法,请 不要使用 panic。)
仅在main() 中调用其中一个 os.Exit 或者 log.Fatal*。所有其他函数应将错误返回到信号失败中。
| Bad | Good |
|---|---|
| |
原则上:退出的具有多种功能的程序存在一些问题:
- 不明显的控制流:任何函数都可以退出程序,因此很难对控制流进行推理。
- 难以测试:退出程序的函数也将退出调用它的测试。这使得函数很难测试,并引入了跳过
go test尚未运行的其他测试的风险。 - 跳过清理:当函数退出程序时,会跳过已经进入
defer队列里的函数调用。这增加了跳过重要清理任务的风险。
一次性退出
如果可能的话,你的main()函数中 最多一次 调用 os.Exit或者log.Fatal。如果有多个错误场景停止程序执行,请将该逻辑放在单独的函数下并从中返回错误。 这会缩短 main() 函数,并将所有关键业务逻辑放入一个单独的、可测试的函数中。
| Bad | Good |
|---|---|
| |
上面的示例使用log.Fatal,但该指南也适用于os.Exit或任何调用os.Exit的库代码。
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}您可以根据需要更改run()的签名。例如,如果您的程序必须使用特定的失败退出代码退出,run()可能会返回退出代码而不是错误。这也允许单元测试直接验证此行为。
func main() {
os.Exit(run(args))
}
func run() (exitCode int) {
// ...
}请注意,这些示例中使用的run()函数并不是强制性的。 run()函数的名称、签名和设置具有灵活性。除其他外,您可以:
- 接受未分析的命令行参数 (e.g.,
run(os.Args[1:])) - 解析
main()中的命令行参数并将其传递到run - 使用自定义错误类型将退出代码传回
main() - 将业务逻辑置于不同的抽象层
package main
本指南只要求在main()中有一个位置负责实际的退出流程。
更新日志
2025/9/4 15:48
查看所有更新日志
a059b-新增Uber的Go语言规范于
