Go 语言如何实现error的优雅处理

我撰写本文,是为了回应别处一个文章的说法。具体来说,就是错误处理的方式太“不优雅”。

go语言的错误处理方式,往往被新接触go语言的新人所诟病。注意哦,我说的可不是“异常处理”。Go语言中是没有异常的,尽管你会说Go语言不是使用panic和recover来处理异常的么。但是,我必须明确指出,我是反对你这种说法的。经常使用这种说法,其实是反设计模式的。你的程序中出现某种错误时,Go只是使用一种类似这种处理方式去处理罢了。

Go的作者们想了很多种处理error的方案。我想他们肯定是权衡再三,从中选取了一个相对最好的解决方案,使其简单而又优雅。

我们知道,GO是可以返回多值结果的。按照惯例,如果有错误发生,函数将返回一个错误,并且该错误是作为最后一个返回值返回的。

func ETPhoneHome(msg string) (string, error) {
    // implementation
}

一个error 类型如下:

type error interface {
    Error() string
}

这意味着,任何实现这个将string作为返回值的Error()方法的类型,都实现了error 接口。string返回值可以描述错误发生的详情。如果觉得对此有所疑惑的话,可以回顾下Go接口的相关内容

在很多Go的代码中,你会见到类似如下的代码段:

response, err := ETPhoneHome("I can't fly this bike forever!")
if err != nil {
    // handle the error, often:
    return err
}
// do something with response

Ruby中,你只需知道某个方法可能会导致异常。在方法名中使用(ex: model.save!) ,这样的标识标明该方法可能会抛出某种异常。但是除此之外,异常纠结什么时候发生,发生在什么具体位置,具体发生的是设么用的异常,这些都只能听天由命的靠运气碰碰了。因此,在可中断的开发语言中,在运行时使得异常可见,这种方式显得更为普遍。Go(与Swift和Java一样)会尝试告诉你可能的的所有错误情况,以限制运行时发生意外的行为。

Go作者认为,并不是所有的异常都是例外。不是所有的错误都应该使你的应用程序崩溃。我当然同意,如果您可以从错误中正常恢复,您应该这样做。您的应用程序将稳定而健壮。当然,说起来容易,做起来难。这需要,程序员自己去做更多的努力。在Ruby中,你可以确保一个异常永远不会发生,继续前进。也就是说,直到客户打电话给你抱怨你的应用程序出问题了,你才会有所察觉到发生了异常。


流程控制

Rob Pike 还写道:

Go语言中的errors处理,不会“遮蔽”流程控制。

因为,在Go中,要么1)error立马被处理或者被忽略,要么2)error会被返回给调用者处,从而你可以跟踪error的路径信息。

然而,在很多其他语言中,流程控制却并不会向上述所说的这样“清晰”。以swift为例:

// Swift
do {
    // If successful, the happy path is now nested.
} catch (let error as NSError) {
    // handle error
}

按自己的方式来编码

你可以使用下面这种方式处理error

if err != nil {
    // handle the error
}

但这并意味着这样的代码就应该写的到处都是。你可以尽情的按照自己的方式来。将error处理代理给某个函数或者对象。毕竟,你可是个牛叉叉的编程高手,对吧?写个抽象搞定它。

例如,我在用GO语言编写命令行工具时,经常使用下面这个函数:

func checkErr(err error) {
    if err != nil {
        fmt.Println("ERROR:", err)
        os.Exit(1)
    }
}

// 或者作为一个常用的用户对重新编码:
func checkErr(err error) {
    if err != nil {
        log.Fatal("ERROR:", err)
    }
}

命令行工具汇总,它效果不错,但是,在web服务端上,可就不咋地咯。

Rob Pike建议对于使用帮助类的函数,最好就不要这样使用了。好好的拜读一下Pike的博客(https://blog.golang.org/errors-are-values),对于如何避免大量的error处理,该博客中给出了大量的实例。


结论

希望我已经说清楚了,GO的error处理方式并不那么糟糕。我个人,其实倒是觉得Go的处理方式蛮优雅的。个人认为,很多刚接触GO的“新人”,之所以一脸懵逼,是因为Go很多标准库都会有error返回值。这就意味着,你不得不去处理这些error。理所当然,它就需要程序员为了error,花费费额外的精力。但是,从另一方面来说,这也会使得程序员更能将软件写的更加健壮、更加稳定。这其实不是坏事嘛,对吧?

更新

关于Reddit和Counter,当前有各种探讨。

像我这样,本身之前从事后端动态语言的编程人员,慢慢的就将诶和搜了Go的error处理方式。但是,之前使用Haskell等类似开发语言的同学,可能往往则是持相反的观点。

没有更多推荐了,返回首页