- Learning Functional Programming in Go
- Lex Sheehan
- 303字
- 2025-02-27 05:14:35
Digging deeper into error handling
In Java, when an exception occurs in a method, the process of creating the exception object and handing it over to the runtime environment is called throwing an exception.
The normal flow of the program halts when this happens, and JRE tries to find a handler in the call stack that can process the raised exception.
The exception object contains a lot of debugging information, such as the line number where the exception occurred, type of exception, the method hierarchy, call stack, and so on.
Dozens of common exception handling antipatterns exist in Java largely due to the design and misunderstanding of proper use of Java's type hierarchy.
"Don’t just check errors, handle them gracefully."
- Dave Cheney
Rather than asserting the error is a specific type or value and passing up the line, we can assert that the error implements a particular behavior:
type errorBehavior interface {
Retryable() bool
}
func IsRetryable(err error) bool {
eb, ok := err.(errorBehavior)
return ok && eb.Retryable()
}
If the IsRetryable error occurs, then the caller would know they can retry the operation that generated the error. The caller does not need to import the library that implements the thrown error and attempt to understand the intricacies of its type hierarchy to handle the error properly.
The github.com/pkg/errors package allows you to wrap errors with context so that later you can recover the cause like this:
func IsRetryable(err error) bool {
eb, ok := errors.Cause(err).(errorBehavior)
return ok && eb.Retryable()
}
Once the error value has been inspected, it should be handled once. Repackaging the error and throwing it up for another handler to deal with is not considered a best practice in Go.