Simple cases are already simple. It's the harder spots that need help.
Simple cases are already simple. It's the harder spots that need help.
func whatev() (err error) {
defer helper(&err, "mightFail context to help debugging", some, vars)
try(mightFail())
defer helper(&err, "alsoMightFail context to help debugging", some, vars)
try(alsoMightFail())
}
if the second call fails, your error has now been wrapped twice.There are of course ways to deal with this. You can add a "already wrapped" sentinel of some kind (say, a * bool that they check and modify), or use a `helper := once(helperfn)` to do that same kind of thing internally... but that's more verbose. And can't be done generically without reflection (though `func(*error, string, ...interface{})` would be quite common, which might be good enough). Inline handlers shown in many examples are even worse since they can't be composed as easily, so you'd probably end up maintaining a bool var between all handlers. By hand.
They both also require naming your `error` return value, which requires naming all returned values, which has been a depressingly large source of shadowing and not-initialized-value mistakes in my experience. Improperly-initialized values especially, as those tend to cause problems a fair distance from the cause, and sometimes go unnoticed for quite a while.
tl;dr it adds more easily-forgotten boilerplate to non-trivial error handling, and makes it and related code more mistake-prone. I'd rather have `return fmt.Errorf(err, ...)`, it fairly naturally resists growing more complex as your func grows more complex.