Will this new Go error handling proposal solve the problem?

Hello everyone, I am fried fish.

A major feature of the Go language is its error mechanism, so basically all error handling proposals or discussions I will review and learn, and develop different thinking horizons and solutions.

What I share today is the proposal " Simple Error Handling for Go 2 " proposed by @Cristo García , slightly modified, let's learn and discuss with Jianyu!

Go must still be Go

The core point of this proposal is that Go must still be Go, which means that the transformation of error handling needs to meet the following principles:

  • Add as little syntax as possible.
  • Be as clear and convenient as possible.

The "I" in this article refer to the proposal author @Cristo García, not the frying fish I'm learning.

original idea

The original proposal author @PeterRk proposed the following ideas:

func getDivisorFromDB(key string) (uint, error) {
	//...
}

func GetDivisor(key string) (uint, error) {
	exit := func(err error) (uint, error) {
		return 1, fmt.Errorf("fail to get divisor with key \"%s\": %v", key, err)
	}

	divisor := check(getDivisorFromDB(key), exit)

	//...
	return divisor, nil
}
复制代码

Example of use:

divisor := check(getDivisorFromDB(key), exit) 
复制代码

Equivalent to existing:

divisor, err := getDivisorFromDB(key)
if err != nil {
	return exit(err)  //return err
}
复制代码

Proposal authors think this is the right direction, and we can improve it (implication: it's not good enough).

What's the question

There are two problems with this original idea:

  • Contains an ambiguous return statement.
  • Sometimes abstraction is unnecessary and makes code harder to read.

new idea

For this new idea needs to solve the above two problems, @Cristo García hopes to achieve better results. With a simple modification to the syntax, we add the or keyword.

The following examples can be obtained:

divisor, err := getDivisorFromDB(key) or return exit(err)
复制代码

The newly added or keyword will check whether the last returned value (which must be an error type) is different from nil. If not, the function on the right will be executed.

We can also omit return and the code will continue to execute. It will be discarded like in regular Go code, so that the function is more reusable.

The following example:

func GetDivisor(key string) (divisor uint, err error) {
	divisor, err = getDivisorFromDB(key) or return
	return
}
复制代码

That is, the or return statement is not followed by anything, it is possible, and it will be discarded by default.

Special scene: defer

This section is just for debate, but we can take this opportunity to add error checking to defer and see if we can do something and get a new way of handling it.

Core idea: If we can not save the returned error in a variable, and make it or get triggered in defer, it will be very interesting.

Example 1 below:

defer f.Close() or return errHdl("", fmt.Errorf("couldn't close file"))
复制代码

If the variable is not explicitly declared, if the return value is of the wrong type and not equal to nil, the function on the right side of or return is automatically called and processed.

Example 2 below:

defer err := f.Close() or return errHdl("couldn't close file", err)
复制代码

Define the variable err variable that accepts errors, which can be used by directly passing parameters into the input parameters of the function errHdl through the or return syntax.

result

A new or return syntax has been added and then compared with the original error handling mechanism to see how.

new:

func Foo(path string) ([]byte, error) {
	errHdlr := func(reason string, err error) ([]byte, error) {
		return nil, fmt.Errorf("foo %s %w", reason, err)
	}
	
	f, err := os.Open(path) or return errHdlr("couldn't open file", err)
	defer f.Close() or return errHdl("", fmt.Errorf("couldn't close file"))
	result, err := io.ReadAll(f) or return errHdlr("couldn't read from file " + path, err)
	return result, nil
}
复制代码

old:

func Foo(path string) ([]byte, error) {
	f, err := os.Open(path)
  	if err != nil {
    		return nil, fmt.Errorf("foo %s %w", "couldn't open file", err)
  	}
  	result, err := io.ReadAll(f)
  	if err != nil {
    		return nil, fmt.Errorf("foo %s %w", "couldn't read from file " + path, err)
  	}
  	err = f.Close()
  	if err != nil {
    		return nil, fmt.Errorf("foo %s %w", "couldn't close the file " + path, err)
  	}
  	return result, nil
}
复制代码

This is a very simple example, but we can already see the benefits. A programmer who is reading code can even focus on the left and ignore error handling.

After formatting the code with gofmt, it is also more beautiful.

The following example:

f, err := os.Open(path)      or return errHdlr("couldn't open file", err)
defer f.Close()              or return errHdl("", fmt.Errorf("couldn't close file"))
result, err := io.ReadAll(f) or return errHdlr("couldn't read from file " + path, err)
复制代码

Quite right.

Summarize

In this new proposal, the author is in the stage of solicitation of comments. It mainly promotes various ideas such as the or keyword and variables can be passed to the function on the right (I also shared a proposal for functions and expressions on the left some time ago).

The author's purpose is to make it as convenient as possible, and not to write the if err != nil that everyone has complained about in the past, so as to achieve more conciseness.

What do you think of this proposal? Welcome to exchange and discuss in the comment area.

The article is continuously updated, you can read it on WeChat by searching [Brain Fried Fish]. This article has been included on GitHub github.com/eddycjy/blo… If you are learning Go language, you can see the Go learning map and route . Welcome to Star to urge you to update.

Go Book Series

read more

Guess you like

Origin juejin.im/post/7119709660445147150