转载

Go的错误处理是优雅的

Go的初学者经常批评Go的错误处理机制。请注意,我没有说异常处理。Go没有异常,虽然可以用 panic and recover 的模拟它和恢复。但我强烈建议反对。这是一个反模式,如果使用过于频繁。我记得上个月七牛的许式伟在上海GopherMeetup上也提到过这个问题。

Go允许多个返回值,按照惯例,如果事情会出错,函数返回一个错误,因为它的最后一个返回值

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

error type:

type error interface {     Error() string }
typeerrorinterface{     Error()string }

这意味着任何类型实现的方法称为Error()

返回一个字符串,实现了错误的接口。返回的字符串应该说明了什么问题。看看Go的 interfaces ,如果你感到困惑。

In a lot of sample Go code, you see:

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

A lot of programmers have beef with this seemingly constant nil checking:

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

看下Java的处理:

// Java try {     String response = ET.phoneHome("I hate white lab coats.")     System.out.println(response); } catch(Exception e) {     System.out.println("Exception thrown  :" + e); }
// Java try{     Stringresponse=ET.phoneHome("I hate white lab coats.")     System.out.println(response); }catch(Exceptione){     System.out.println("Exception thrown  :"+e); }

shiny new Swift 2:

// Swift // Option 1 do {     let response = try ETPhoneHome("Those are guns not walkie talkies!")     // process response } catch (let error as NSError) {     print("oops", error) }  // Option 2: assign to an optional variable let response = try? ETPhoneHome("Halp!")  // Option 3: crash the app if an error is thrown let response = try! ETPhoneHome("BOOM")
// Swift // Option 1 do{  letresponse=tryETPhoneHome("Those are guns not walkie talkies!")  // process response }catch(leterrorasNSError){  print("oops",error) } // Option 2: assign to an optional variable let response=try?ETPhoneHome("Halp!") // Option 3: crash the app if an error is thrown let response=try!ETPhoneHome("BOOM") 

看完了Java和swift再看看Go

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

Errors are Values:

(下面不知道怎么翻译比较好,等我想好了再翻译。)

The Go team purposefully chose errors to be values.

Values can be programmed, and since errors are values, errors can be programmed. Errors are not like exceptions. There’s nothing special about them, whereas an unhandled exception can crash your program.

In Go, functions pass errors around just like any other type. Because indeed they are just a type.

Exceptions, on the other hand, are unique. They can wreak havoc on your program if left unchecked. (I realize there are technical differences between Swift errors and exceptions. But the semantics are the same in regards to error handling.)

So, if we can handle errors using simple values, why do we need this special exception stuff? Why not leave that complexity out of the language?

I’m not sure if the Go authors reached that exact conclusion, but it make sense to me.

Flow of Control

Rob Pike also writes:

Error handling [in Go] does not obscure the flow of control.

Swift 的流程控制:

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

对于简单业务也许可以使用这个嵌套,但是,如果有更多像if / else的嵌套呢?代码就会难以阅读。

Swift 2引入了guard statement来提高流程控制。

// Swift guard let result = try? SomeFunction() else {     return } // result is in scope, proceed with happy path
// Swift guard letresult=try?SomeFunction()else{     return } // result is in scope, proceed with happy path

然而你不能用guard 在swift中来检查错误。但是,如果你要来检查错误或者避免嵌套?你就不能这么做了。

// Swift var result: Any? do {     result = SomeFunction() } catch (let error as NSError) {     // handle error } // proceed with happy path
// Swift varresult:Any? do{     result=SomeFunction() }catch(leterrorasNSError){     // handle error } // proceed with happy path

然而看看Go是多么的简洁。

// Go result, err := SomeFunction() if err != nil {     // handle the error } // trot down the happy path
// Go result,err:=SomeFunction() iferr!=nil{     // handle the error } // trot down the happy path

Code Your Way Out of It

Just because you can do this

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

As a simple example, when I write CLI tools in Go, I usually have a function:

func checkErr(err error) {     if err != nil {         fmt.Println("ERROR:", err)         os.Exit(1)     } }  // Or as a kind user on reddit refactored: func checkErr(err error) {     if err != nil {         log.Fatal("ERROR:", err)     } }
func checkErr(errerror){  iferr!=nil{   fmt.Println("ERROR:",err)   os.Exit(1)  } } // Or as a kind user on reddit refactored: func checkErr(errerror){  iferr!=nil{   log.Fatal("ERROR:",err)  } } 

总结:

我希望我能表达出了Go错误机制并不是大家想象的那么糟糕。至少我觉得是很优雅的。

Go的错误处理是优雅的

正文到此结束
Loading...