Error handling has been a very controversial area of Go, and people have contributed all sorts of ideas to proposals in that category. Recently, I also found an interesting technical proposal: the left-hand side function; and a new idea for Go+.

Go’s new proposal: the left-hand side function

With the existing Go1 error handling mechanism, we generally need to write a lot of if err ! = nil logic.

Some people laughingly claim that 50 out of 100 lines of code are the following.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func main() {
 x, err := foo()
 if err != nil {
   // handle error
 }
 y, err := foo()
 if err != nil {
   // handle error
 }
 z, err := foo()
 if err != nil {
   // handle error
 }
 s, err := foo()
 if err != nil {
   // handle error
 }
}

So several people in the community came up with the idea of left-handed functions.

We hope to solve the error handling problem by reducing the extra 3 lines of code written each time and achieving a consistent error handling method.

The following proposal was involved.

The new code in the proposal is as follows.

1
fmt.Errof("%v, %w", a, err) := simple()

Simplified writing.

1
errorHandle(err) = io.Copy(w, r)

A new way of thinking about handling errors is to add a layer (the universal software architecture way of handling them) and use the left-hand function to handle all errors.

Go+: Error Expressions

A member of the Go-related group: Go+, has also made its own “ErrWrap expressions” error handling solution, which was discussed by a certain number of people in the previous proposal, so you can evaluate it together.

Introduction to expressions

The core idea is to add a syntactic mechanism of expressions to error handling. As follows.

1
2
3
expr! // panic if err
expr? // return if err
expr?:defval // use defval if err

We’ll expand on them one by one.

The expression expr! checks if valN is zero. If not, it will panic. the corresponding Go code.

1
2
3
4
5
val1, val2, ..., valN1, valN := expr
if valN != nil {
    panic(errors.NewFrame(valN, ...))
}
val1, val2, ..., valN1 // value of `expr!`

The expression expr? checks if valN is nil, and if not, it returns an error. Corresponding Go code.

1
2
3
4
5
6
val1, val2, ..., valN1, valN := expr
if valN != nil {
    _ret_err = errors.NewFrame(valN, ...)
    return
}
val1, val2, ..., valN1 // value of `expr?`

The expression expr?:defval checks if valN is nil. if not, it uses defval as the value of expr. Corresponding Go code.

1
2
3
4
5
val1, val2 := expr
if val2 != nil {
    val1 = defval
}
val1 // value of `expr?:defval`

Demonstration code

Specific sample code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import (
 "strconv"
)

func add(x, y string) (int, error) {
 return strconv.Atoi(x)? + strconv.Atoi(y)?, nil
}

func addSafe(x, y string) int {
 return strconv.Atoi(x)?:0 + strconv.Atoi(y)?:0
}

println(`add("100", "23"):`, add("100", "23")!)

sum, err := add("10", "abc")
println(`add("10", "abc"):`, sum, err)

println(`addSafe("10", "abc"):`, addSafe("10", "abc"))

Output.

1
2
3
4
5
6
7
8
add("100", "23"): 123
add("10", "abc"): 0 strconv.Atoi: parsing "abc": invalid syntax

===> errors stack:
main.add("10", "abc")
 /Users/xsw/goplus/tutorial/15-ErrWrap/err_wrap.gop:6 strconv.Atoi(y)?

addSafe("10", "abc"): 10

In addition to the optimization of the mechanism for error handling based on expressions, information tracking of the error stack has been added.