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 in GitHub github.com/eddycjy/blog . To learn Go language, you can see the Go learning map and route . Welcome to Star to urge you to update.
Go Book Series
- Introduction to the Go language series: a preliminary exploration of the actual combat of the Go project
- Go Programming Journey: Deep Dive into Go Projects
- Go Language Design Philosophy: Understanding Go Why and Design Thinking
- Go Language Advanced Tour: Go deeper into the Go source code
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。