Handle and Check - Let's Not

There’s a new error handling design proposed here. It’s…. not great.

Handle is a new keyword that basically defines a translation that can be applied to errors returned from the current function:

func printSum(a, b string) error {
	handle err { return fmt.Errorf("error summing %v and %v: %v", a, b, err ) }
	x := check strconv.Atoi(a)
	y := check strconv.Atoi(b)
	fmt.Println("result:", x + y)
	return nil
}

Check applies the handler and returns if the error passed into it is not nil, otherwise it returns the non-error value.

Handle, in my opinion is kind of useless. We can already do this today with functions thusly:

func printSum(a, b string) (err error) {
	check := func(err error) error { 
        return fmt.Errorf("error summing %v and %v: %v", a, b, err )
    }
	x, err := strconv.Atoi(a)
    if err != nil {
        return check(err)
    }
	y, err := strconv.Atoi(b)
    if err != nil {
        return check(err)
    }
	fmt.Println("result:", x + y)
	return nil
}

That does literally the same thing as check and handle above.

The stated reason for adding check and handle is that too many people just write “return err” and don’t customize the error at all, which means somewhere at the top of your program, you get this inscrutable error from deep in the bowels of your code, and you have no idea what it actually means.

It’s trivial to write code that does most of what check and handle do… and no one’s doing it today (or at least, not often). So why add this complexity?

Check and handle actually make error handling worse. With the check and handle code, there’s no required “error handling scope” after the calls to add context to the error, log it, clean up, etc. With the current code, I always have an if statement that I can easily slot more lines into, in order to make the error more useful and do other things on the error path. With check, that space in the code doesn’t exist. There’s a barrier to making that code handle errors better - now you have to remove check and swap in an if statement. Yes, you can add a new handle section, but that applies globally to any further errors returns in the function, not just for this one specific error. Most of the time I want to add information about one specific error case.

So, for example, in the code above, I would want a different error message for A failing Atoi vs. B failing Atoi…. because in real code, which one is the problem may not be obvious if the error message just says “either A or B is a problem”.

Yes, if err != nil { constitutes a lot of Go code. That’s ok. That’s actually good. Error handling is extremely important. Check and handle don’t make error handling better. I suspect they’ll actually make it worse.

A refrain I often state about changes requested for Go is that most of them just involve avoiding an if statement or a loop. This is one of them. That’s not a good enough reason to change the language, in my opinion.

w