The go FQA states:
It's a good idea for functions that return errors always to use the error type in their signature (as we did above) rather than a concrete type such as *MyError, to help guarantee the error is created correctly. As an example, os.Open returns an error even though, if not nil, it's always of concrete type *os.PathError.
But this article claims that sometimes it is OK to return with a concrete type:
(It's usually a mistake to pass back the concrete type of an error rather than error, for reasons discussed in the Go FAQ, but it's the right thing to do here because
ServeHTTP
is the only place that sees the value and uses its contents.)
My question is, what are the benefits of returning with error
rather than the concrete type, how does it help guarantee that the error is created correctly, and why is it okay to return the concrete type when the error is not used in different places?
This is related to error
being an interface. An interface contains a pointer to the value it contains and the type of that value. An interface is nil only if both of those values are nil. So, if you return a concrete error type from a function, and then return an error
, that error will not be nil.
type MyError string
func (e MyError) Error() string {return string(e)}
func f() *MyError {
return nil
}
func g() error {
return f()
}
func main() {
x:=g()
if x==nil {
fmt.Println("nil")
}
}
In the example above, even though *MyError
value is nil, the return value of g()
is not, because it contains an interface with type *MyError
and value nil.
This is a problem for all functions returning an interface value, and most commonly observed with error
types. So do not declare functions returning concrete error values unless those functions have very limited use, such as unexported functions.