我为什么要叫os.在主功能中最多退出一次



我开始了一份新工作,我们被要求使用Ubers Go编码标准。我不确定他们的一条题为";退出一次":

如果可能,请在main()中最多调用os.Exitlog.Fatal一次。如果存在多个暂停程序执行的错误场景,请将该逻辑置于一个单独的函数下,并从中返回错误

这难道不意味着将main()卸载到另一个函数(run()(中吗?这在我看来有点多余。优步的做法有什么好处?

我不熟悉优步的整个Go编码标准,但这条特别的建议是合理的。os.Exit的一个问题是,它非常残酷地结束了程序,而没有遵守任何延迟的函数调用:

Exit会导致当前程序退出,并带有给定的状态代码。按照惯例,代码零表示成功,非零表示错误。程序立即终止延迟函数不运行

(我的重点(

但是,那些延迟的函数调用可能负责重要的清理任务。考虑优步的示例代码片段:

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()
// If we call log.Fatal after this line,
// f.Close will not be called.
b, err := ioutil.ReadAll(f)
if err != nil {
log.Fatal(err)
}
// ...
}

如果ioutil.ReadAll返回非零错误,则调用log.Fatal;并且由于log.Fatal在后台调用os.Exit,所以对f.Close的延迟调用将不会运行。在这种特殊的情况下,情况并没有那么严重,但想象一下,延迟调用涉及一些清理,比如删除文件;你会让你的磁盘处于不干净的状态。有关该主题的更多信息,请参阅Go Time播客第112集,其中讨论了这些考虑因素。

因此,避开os.Exitlog.Fatal等是个好主意;深";在你的节目中。优步Go编码标准中描述的run函数允许在程序执行结束前按原样运行延迟调用(可能具有非零状态代码(:

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
}
// ...
}

这种方法的另一个好处是,尽管main函数本身不容易测试,但您可以在设计这样的run函数时考虑到可测试性;请参阅Mat Ryer关于该主题的博客文章。

最新更新