Should I use %s or %v to format errors?
TL;DR; Neither. Use %w
in 99.99% of cases. In the other 0.001% of cases, %v
and %s
probably “should” behave the same, except when the error value is nil
, but there are no guarantees. The friendlier output of %v
for nil
errors may be reason to prefer %v
(see below).
Now for details:
Use %w
instead of %v
or %s
:
As of Go 1.13 (or earlier if you use golang.org/x/xerrors), you can use the %w
verb, only for error
values, which wraps the error such that it can later be unwrapped with errors.Unwrap
, and so that it can be considered with errors.Is
and errors.As
.
The only times this is inappropriate:
- You must support an older version of Go, and
xerrors
is not an option. - You want to create a unique error, and not wrap an existing one. This might be appropriate, for instance, if you get a
Not found
error from your database when searching for a user, and want to convert this to anUnauthorized
response. In such a case, it’s rare that you’d be using the original error value with any formatting verb, though.
Okay, so what about %v
and %s
?
The details for how %s
and %v
are implemented are available in the docs. I’ve highlighted the parts relevant to your question.
If the operand is a reflect.Value, the operand is replaced by the concrete value that it holds, and printing continues with the next rule.
If an operand implements the Formatter interface, it will be invoked. Formatter provides fine control of formatting.
If the %v verb is used with the # flag (%#v) and the operand implements the GoStringer interface, that will be invoked.
If the format (which is implicitly %v for Println etc.) is valid for a string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
To summarize, the fmt.*f
functions will:
- Look for a
Format()
method, and if it exists, they’ll call it. - Look for a
Error()
method, and if it exists, they’ll call it. - Look for a
String()
method, and if it exists, call it. - Use some default formatting.
So in practice, this means that %s
and %v
are identical, except when a Format()
method exists on the error type (or when the error is nil
). When an error does have a Format()
method, one might hope that it would produce the same output with %s
, %v
, and err.Error()
, but since this is up to the implementation of the error, there are no guarantees, and thus no “right answer” here.
And finally, if your error type supports the %+v
verb variant, then you will, of course, need to use that, if you desire the detailed output.
nil
values
While it’s rare to (intentionally) call fmt.*f
on a nil
error, the behavior does differ between %s
and %v
:
%s: %!s(<nil>)
%v: <nil>
Playground link