0. 错误处理的编码风格

检查某个子函数是否失败后,我们通常将处理失败的逻辑代码放在处理成功的代码之前。如果某个错误会导致函数返回,那么成功的逻辑代码不应该放在else中,而应直接放在函数体中。

1. 错误传播

函数某个子程序的失败,会变成该函数的失败

resp, err := http.Get(url)
if err != nil{
    return nill, err
}

或是构造新的错误信息返回给调用者

doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil {
    return nil, fmt.Errorf("parsing %s as HTML: %v", url,err)
}

一般而言,被调函数f(x)会将调用信息和参数信息作为发生错误时的上下文放在错误信息中并返回给调用者,调用者需要添加一些错误信息中不包含的信息。

2. 重试失败的操作

如果错误的发生是偶然的,或由不可预知的问题导致的。此时可重新尝试失败的操作,但是在重试时,要限制重试的时间间隔或重试的时间次数,防止无限制的重试。

func WaitForServer(url string) error {
    const timeout = 1 * time.Minute
    deadline := time.Now().Add(timeout)
    for tries := 0; time.Now().Before(deadline); tries++ {
        _, err := http.Head(url)
        if err == nil {
            return nil // success
        }
        log.Printf("server not responding (%s);retrying…", err)
        time.Sleep(time.Second << uint(tries)) // exponential back-off
    }
    return fmt.Errorf("server %s failed to respond after %s", url, timeout)
}

3. 输出错误信息并结束程序

这种策略应只在main中使用,对于库函数而言,应仅向上传播错误,除非该错误意味着程序内部包含不一致性,即遇到了bug,才能在库函数中结束程序

// (In function main.)
if err := WaitForServer(url); err != nil {
    fmt.Fprintf(os.Stderr, "Site is down: %v\n", err)
    os.Exit(1)
}

4. 只输出错误信息

只输出错误信息,不需要中断函数的执行

if err := Ping(); err != nil {
    log.Printf("ping failed: %v; networking disabled",err)
}

5. 直接忽略错误

dir, err := ioutil.TempDir("", "scratch")
if err != nil {
    return fmt.Errorf("failed to create temp dir: %v",err)
} // ...use temp dir…
os.RemoveAll(dir) // ignore errors; $TMPDIR is cleaned periodically