• 7.2. 错误只处理一次

    7.2. 错误只处理一次

    最后,我想提一下你应该只处理错误一次。 处理错误意味着检查错误值并做出单一决定。

    1. // WriteAll writes the contents of buf to the supplied writer.
    2. func WriteAll(w io.Writer, buf []byte) {
    3. w.Write(buf)
    4. }

    如果你做出的决定少于一个,则忽略该错误。 正如我们在这里看到的那样, w.WriteAll 的错误被丢弃。

    但是,针对单个错误做出多个决策也是有问题的。 以下是我经常遇到的代码。

    1. func WriteAll(w io.Writer, buf []byte) error {
    2. _, err := w.Write(buf)
    3. if err != nil {
    4. log.Println("unable to write:", err) // annotated error goes to log file
    5. return err // unannotated error returned to caller
    6. }
    7. return nil
    8. }

    在此示例中,如果在 w.Write 期间发生错误,则会写入日志文件,注明错误发生的文件与行数,并且错误也会返回给调用者,调用者可能会记录该错误并将其返回到上一级,一直回到程序的顶部。

    调用者可能正在做同样的事情

    1. func WriteConfig(w io.Writer, conf *Config) error {
    2. buf, err := json.Marshal(conf)
    3. if err != nil {
    4. log.Printf("could not marshal config: %v", err)
    5. return err
    6. }
    7. if err := WriteAll(w, buf); err != nil {
    8. log.Println("could not write config: %v", err)
    9. return err
    10. }
    11. return nil
    12. }

    因此你在日志文件中得到一堆重复的内容,

    1. unable to write: io.EOF
    2. could not write config: io.EOF

    但在程序的顶部,虽然得到了原始错误,但没有相关内容。

    1. err := WriteConfig(f, &conf)
    2. fmt.Println(err) // io.EOF

    我想深入研究这一点,因为作为个人偏好, 我并没有看到 logging 和返回的问题。

    1. func WriteConfig(w io.Writer, conf *Config) error {
    2. buf, err := json.Marshal(conf)
    3. if err != nil {
    4. log.Printf("could not marshal config: %v", err)
    5. // oops, forgot to return
    6. }
    7. if err := WriteAll(w, buf); err != nil {
    8. log.Println("could not write config: %v", err)
    9. return err
    10. }
    11. return nil
    12. }

    很多问题是程序员忘记从错误中返回。正如我们之前谈到的那样,Go 语言风格是使用 guard clauses 以及检查前提条件作为函数进展并提前返回。

    在这个例子中,作者检查了错误,记录了它,但忘了返回。这就引起了一个微妙的错误。

    Go 语言中的错误处理规定,如果出现错误,你不能对其他返回值的内容做出任何假设。由于 JSON 解析失败,buf 的内容未知,可能它什么都没有,但更糟的是它可能包含解析的 JSON 片段部分。

    由于程序员在检查并记录错误后忘记返回,因此损坏的缓冲区将传递给 WriteAll,这可能会成功,因此配置文件将被错误地写入。但是,该函数会正常返回,并且发生问题的唯一日志行是有关 JSON 解析错误,而与写入配置失败有关。