My main critique with Error Values is the Formatter
interface.
I fear that nearly every format function will be written in the exact same fashion:
func (e *MyErr) Format(p errors.Printer) (next error) {
p.Print(/* short error string */)
if p.Detail() {
p.Print(/* details */)
p.Print(/* details */)
p.Print(/* details */)
}
return /* caused by */
}
But we already have something to replace short error string
with, and
something to replace caused by
with. The only thing that isn't provided is the details
.
Instead of a Formatter
, we should have a Detailer
, and let the library printing
the error handle the rest.
Detailer could look something like...
type Detailer interface {
Detail() []string
}
Any advantage provided by using p.Printf
can be equally achieved using fmt.Sprintf
.
A Formatter
was wanted to allow for localization. This is still allowed under
using Detailer
instead, as localization directives may be placed in in the
string slice if you chose to do so, just as you got to choose if you wanted
to put localization directives in your p.Print
statements.
Then, the library printing the error (fmt
, golang.org/x/text/message
, etc) may format as needed.
For the same effect as in the overview.
This is of course simplified quite a lot, I'm writing this at 3:30am and don't want to be thinking too hard!
func printErr(err error) {
fmt.Println("--- %s", err.Error())
if det, ok := err.(errors.Detailer); ok {
for _, line := range det.Detail() {
fmt.Println(" " + line)
}
}
if wrap, ok := err.(errors.Wrapper); ok {
causedBy := wrap.Unwrap()
if causedBy != nil {
printErr(causedBy)
}
}
}
Thank you for reading, I hope you consider this.
Some more remarks:
errors.Formatter
is bad because this has more chance for programmer error. If the programmer forgets to printError()
, or return the next error, it an extremely negative impact on anything trying to print it. It leaves for too much human error.Using
Detailer
instead ofFormatter
also allows for much more parsable errors. For instance, if I wanted JSON for my error, I can now very easily parse my error to:To do this with
Formatter
, you would need to create a customerrors.Printer
and assume what is error vs what is details, because it would not be clear as to what's what.