Skip to content

Instantly share code, notes, and snippets.

@PeterRK
Last active June 29, 2022 06:33
Show Gist options
  • Save PeterRK/4f59579c1162cdbc28086f6b5f7b4fa2 to your computer and use it in GitHub Desktop.
Save PeterRK/4f59579c1162cdbc28086f6b5f7b4fa2 to your computer and use it in GitHub Desktop.
Simple Error Handling for Go 2

Key Parts of Error Handling

val, err := function()
if err != nil {
	//handler codes...
}

Error handling consists of 3 key parts, trigger, handler and binding rule. In Go 1, trigger is if err != nil, handler is a piece of code in {}, and binding rule is obvious. Handler holds the control flow switch.

Actually, I donot think error handling in Go 1 has any functional problem. The only problem is repetition, especially repetition of triggers. Go 1 allows us to place trigger and assignment in one line, but that does not really solve the problem.

if val, err := function(); err != nil {
	//handler codes...
}

Trigger

Triggers are the same, so it's a good idea to represent the trigger as one word. With more parameters, trigger can becomme more powerful. Trigger with 1 parameter can do nothing more than detect error. Trigger with 2 parameters can specify handler. Trigger with more than 2 parameters can specify a handler chain, or deliver extra parameters to the handler.

check(function, handler1, handler2, handler3)
func handler(path string, err error) error { return fmt.Errorf("%s: %v", path, err); }
check(function, handler, path)

Handler

Handlers are not always the same. What should we do is to make common handlers sharable, leaving special ones unchanged. Traditionaly, we create function for sharing code. The most common error handler, which just pass error to upstream, can be compiler-predefined. Implements of an interface may share one error handler. Main drawback of using function as error handler is function cannot change the control flow of its caller directly.

Binding Rule

A simple clear binding rule may be important than trigger and handler.

for (i := 0; i < 2; i++) {
	handle err {...}	//A
	check function()	//Whould B be invoted?
	handle err {...}	//B
}

Complicated binding rule looks powerful, but help little in fact. When we have to think hard to figure out the consequence of error hanling, error hanling become poison.


Original Feadback

Error handling should act as an easy exit in most cases. A complicated mechanism looks like defer/recover but works differently may be confusing. I suggest a simple way for error handling without new keywords.

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
}

In code above, "check" is a builtin function like "make", accepting 1 or 2 parameters. The first is the function to be checked, the second is the error handler. An error handler is a function having the same output type with the function to be checked. If the error handler is not specified, a default one will be created to pass through error.

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

is equal to

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

Chained error handling is not the common case, because the defer mechanism will handle resouce release. We just want a quick exit when error occurs. If someone really needs chained handling, he can do it explictly in the exit function.


This does not cover all cases of error handling, but improves exiting on error which contributes the most repeated code. Uncommon dedicate operations should be done with old style. If many breaks or continues are need in a loop, that fat loop body should be refactored into one or more functions where the check&exit works.

@griesemer
Copy link

PeterRK thanks for this write-up. I have referenced it from golang/go#32437.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment