More Elegant Golang error handling

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Error handling in Golang is a topic that is often discussed by everyone (the other is generics). The issue of generics, which the RSC has also proposed to include in his plans for this year's plan, has also been updated in 2016, and there are some better options to come up with in the future. In this article we discuss how to provide more friendly and elegant error handling under the current Golang framework.

From the present situation

Golang in the error handling principle, developers have previously specifically published several articles (error handling and Go and Defer, Panic, and Recover, Errors are values) introduced. This paper introduces the error handling in Golang and the error processing mechanism when encountering crashes.

In general, let's take the example of error handling in the official blog:

func Main() {    F, Err := OS.Open("Filename.ext")    if Err != Nil {        Log.Fatal(Err)        //or more simply:        //Return err    }    ...}

There is, of course, another way to simplify the number of lines of code:

func Main() {    ...    if F, Err = OS.Open("Filename.ext"); Err != Nil{        Log.Fatal(Err)    }    ...}

Under normal circumstances, Golang's existing philosophy requires you to handle all error returns manually, which slightly increases the mental burden of the developer. For a discussion of this part of the design, please refer to the reference link provided at the beginning of this article, and do not discuss it much here.

Essentially, the error type in Golang error is an interface type:

type error interface {    Error() string}

You can pass error in the location of the type as long as all values that satisfy this interface definition are passed. A description of the error is also mentioned in Go Proverbs: Errors are values . How do you understand this sentence?

Errors is values

In fact, in practice, you may also find that all the information is very inadequate for Golang. For example, the following:

buf :=  Make([]byte,  -)N, Err := R.Read(buf)buf = buf[:N]if Err == io.EOF { Log.Fatal("Read failed:", Err)}

In fact, this will only print information 2017/02/08 13:53:54 read failed:EOF , which does not make any sense to debug and analyze errors in our real environment, and we can get very limited information when we look at the logs for error messages.

Consequently, some of the error-handling forms that provide a contextual approach are common in many class libraries:

err := os.Remove("/tmp/nonexist")log.Println(err)

The output is:

2017/02/08 14:09:22 remove /tmp/nonexist: no such file or directory

This approach provides a more intuitive contextual information, such as the specific error, or the wrong file, and so on. By looking at the implementation of the remove, we can see:

//Patherror records an error and the operation and file path, that caused it.type Patherror struct {    Op   string    Path string    ERR  Error}func (e *Patherror) Error() string { return e.Op + " " + e.Path + ": " + e.ERR.Error() }//FILE_UNIX.GO implementation for *nix systems//Remove removes the named file or directory.//If There is an error, it would be of type *patherror.func Remove(name string) Error {    //System Call Interface forces us to know    //Whether name is a file or directory.    //Try both:it is cheaper on average than    //doing a Stat plus the right one.    e := Syscall.Unlink(name)    if e == Nil {        return Nil    }    E1 := Syscall.Rmdir(name)    if E1 == Nil {        return Nil    }    //Both failed:figure out which error to return.    //OS X and Linux differ on whether unlink (dir)    //returns EISDIR, so can ' t use that. However,    //Both agree that RmDir (file) returns ENOTDIR,    //So we can use this to decide which the error is real.    //Rmdir might also return Enotdir if given a bad    //file path, Like/etc/passwd/foo, but in the case,    //Both errors is enotdir, so it's okay to    //Use the error from unlink.    if E1 != Syscall.Enotdir {        e = E1    }    return &Patherror{"Remove", name, e}}

In fact, the Golang standard library returns a struct named structure PathError that defines the type of operation, the path, and the original error message, and then Error consolidates all the information through the method.

However, there are also problems, such as the need for separate types of complex classification processing, such as the above example, you need to deal with PathError this problem separately, you may need a separate type deduction:

Err := xxxx()if Err != Nil {    Swtich Err := Err.(type) {     Case *OS.Patherror:        ...    default:        ...    }}

This instead increases the complexity of error handling. At the same time, these errors must become export types and also increase the complexity of the entire system.

Another problem is that we usually want to get more stack information in case of errors, so that we can follow up the fault tracking. In the existing error system, this is relatively complex: it is difficult for you to get the complete call stack through an interface type. At this point, we may need a third-party repository to address these error handling issues.

Another scenario is that we want to be able to attach some information to the error-handling process, which is also relatively cumbersome.

More elegant error handling

Before mentioning the error handling methods and some problems encountered in a variety of practical scenarios, it is recommended to use a third-party library to solve some of the problems: github.com/pkg/errors .

For example, when we are having problems, we can simply use errors.New or errors.Errorf generate an error variable:

err := errors.New("whoops")// orerr := errors.Errorf("whoops: %s", "foo")

When we need additional information, we can use:

cause := errors.New("whoops")err := errors.Wrap(cause, "oh noes")

When you need to get the call stack, you can use:

err := errors.New("whoops")fmt.Printf("%+v", err)

Other recommendations

When we do type deduction above, we find that it may require multiple error types to handle a class of errors, which may be relatively complex in some cases, and many times we can use the interface form to facilitate processing:

type Temporary Interface {    Temporary() BOOL}//IsTemporary Returns True if Err is temporary.func istemporary(Err Error) BOOL {    te, OK := Errors.cause(Err).(Temporary)    return OK && te.Temporary()}

This allows for more convenient error parsing and processing.

Advertising time

We are recruiting new Gopher, fresh graduates or interns are welcome to submit resumes. We are working to standardize the development process, and if you want to improve, it is also a very good opportunity. Resume delivery Kevin [at] yeeuu [dot] com.

Golang
  • Like
  • Tweets
  • +1
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.