Skip to content

Instantly share code, notes, and snippets.

@VictoriaRaymond
Last active Sep 9, 2018
Embed
What would you like to do?
Force check to return error instead of allowing customized logic

This is a response for Go 2 error handling proposal.

The proposed check/handle logic looks strange, as it allows functions to continue in an error condition.

Consider the following example:

func testFunc() error {
  var count int
  handle err {
    count++
  }
  
  a := check getA();
  b := check getB();
  for _, x := range cList {
    c := getC(x)
  }
}

Everytime when I read on a check statement, I have to search back what handlers will be invoked, whether they return the error to the caller or continue to the next line where error was thrown. It costs a lot of mental work to figure out the actual code path. It is bad to allow users to write arbitrary error handlers. It will introduce unnecessary complexity to maintain the code.

As Golang is a language that push for best practices, such as code format, (no) assertions, etc, I believe the error handling should follow the same style. Therefore I propose to force check statement to return error to the caller. The error handler can only do some decoration on the error object to be returned.

Here is a piece of code for example:

func testFunc() error {
  defer func() {
    err := recover() // or maybe a new method for error handling
    return errors.New("testFunc: " + err.Error())
  }
  
  a := must getA();
  b := must getB();
  // ...
}

The must statement here is to force returning the error to the caller. The rest of function will not be executed in such case, except for the defer blocks (as same in Go 1).

The proposal has the following advantages over the existing proposal:

  • It requires one less new keyword.
  • It follows the existing Go logic: panic/defer/recover, which is easier for users to adopt.
  • It encourages users to use traditional if err != nil for customized logic as usual, since cutomized logic probably can't be reused in other places.
@pborman

This comment has been minimized.

Copy link

@pborman pborman commented Aug 29, 2018

If you named your return value then would would not need recover or other new special method:

func testFunc() (err error) {
	defer func() {
		if err != nil {
			err = errs.New("testfunc: " + err.Error())
		}
	}()

	a := must getA()
	b := must getB()
	// ...
}

The equivalent Go 1 code would be:

func testFunc() (err error) {
	defer func() {
		if err != nil {
			err = errs.New("testfunc: " + err.Error())
		}
	}()

	a, err := getA()
	if err != nil {
		return err
	}

	b, err := getB()
	if err != nil {
		return err
	}
}

I would hope we can not introduce a new keyword, if possible.

Of course, if we had a pre-processor:

#define OR_ERR	if err != nil { return err }

	a, err := getA() OR_ERR

Okay, that last suggestion wasn't serious :-)

@leafbebop

This comment has been minimized.

Copy link

@leafbebop leafbebop commented Aug 30, 2018

No, accoding to the draft, in section handler, it says:

If the enclosing function has result parameters, it is a compile-time error if the handler chain for any check is not guaranteed to execute a return statement.

So if there is a check and an error, there is no return to the normal path.

@flaneur2020

This comment has been minimized.

Copy link

@flaneur2020 flaneur2020 commented Sep 9, 2018

👍

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