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.
A few comments: wrap is not the same as next. Having a separate "unwrap" and "next-in-chain" method, as, for instance used in errgo, has been proven to confusing in practice. Dave Cheney's pkg/errors doesn't make this distinction mostly because of this confusion. Reporting the next in chain in a way closely coupled to formatting makes it clear which is used for which, solving this issue. Your suggestion discards this distinction again.
I'm not sure how you envision localization can work in your API. In the Formatter API, the Printer will contain the necessary context to localize a string properly. There is no localization information passed to the Detail method.