Go程序使用os.Exit 或者 log.Fatal* 立即退出 (使用panic不是退出程序的好方法,请 don’t panic.)

仅在main()中调用其中一个 os.Exit 或者 log.Fatal*。所有其他函数应将错误返回到信号失败中。

BadGood
func main() {
  body := readFile(path)
  fmt.Println(body)
}
func readFile(path string) string {
  f, err := os.Open(path)
  if err != nil {
    log.Fatal(err)
  }
  b, err := ioutil.ReadAll(f)
  if err != nil {
    log.Fatal(err)
  }
  return string(b)
}
func main() {
  body, err := readFile(path)
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(body)
}
func readFile(path string) (string, error) {
  f, err := os.Open(path)
  if err != nil {
    return "", err
  }
  b, err := ioutil.ReadAll(f)
  if err != nil {
    return "", err
  }
  return string(b), nil
}

原则上:退出的具有多种功能的程序存在一些问题:

  • 不明显的控制流:任何函数都可以退出程序,因此很难对控制流进行推理。
  • 难以测试:退出程序的函数也将退出调用它的测试。这使得函数很难测试,并引入了跳过 go test 尚未运行的其他测试的风险。
  • 跳过清理:当函数退出程序时,会跳过已经进入defer队列里的函数调用。这增加了跳过重要清理任务的风险。

一次性退出

如果可能的话,你的main()函数中最多一次 调用 os.Exit或者log.Fatal。如果有多个错误场景停止程序执行,请将该逻辑放在单独的函数下并从中返回错误。
这会缩短 main()函数,并将所有关键业务逻辑放入一个单独的、可测试的函数中。

BadGood
package main
func main() {
  args := os.Args[1:]
  if len(args) != 1 {
    log.Fatal("missing file")
  }
  name := args[0]
  f, err := os.Open(name)
  if err != nil {
    log.Fatal(err)
  }
  defer f.Close()
  // 如果我们调用log.Fatal 在这条线之后
  // f.Close 将会被执行.
  b, err := ioutil.ReadAll(f)
  if err != nil {
    log.Fatal(err)
  }
  // ...
}
package main
func main() {
  if err := run(); err != nil {
    log.Fatal(err)
  }
}
func run() error {
  args := os.Args[1:]
  if len(args) != 1 {
    return errors.New("missing file")
  }
  name := args[0]
  f, err := os.Open(name)
  if err != nil {
    return err
  }
  defer f.Close()
  b, err := ioutil.ReadAll(f)
  if err != nil {
    return err
  }
  // ...
}
最后编辑: kuteng  文档更新时间: 2021-05-09 20:12   作者:kuteng