Should I return an error object, or a pointer to an error object?

1,434 views
Skip to first unread message

Brian Candler

unread,
Nov 13, 2019, 12:24:30 PM11/13/19
to golang-nuts
In https://blog.golang.org/error-handling-and-go, it shows the well-known error interface:

type error interface {
    Error() string
}

and it gives an example of a user-defined error:

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

I notice that it's returning a pointer to an error object.  My question is: is there a particular reason for returning a pointer here, rather than a direct instance of an error?

The word "pointer" doesn't appear anywhere in that blog posting.  Clearly a pointer allows the possibility of returning "nil" - but an interface variable can have a nil value too. Without the pointers, it becomes:

func (e errorString) Error() string {
    return e.s
}

func New(text string) error {
    return errorString{text}
}

and this appears to work the same - see a longer example on play (based on someone else's example, but I just removed the pointers).

I'm aware that the method set of *T includes the methods of T, so a pointer functions perfectly well in place of a concrete value to implement an interface.  Also, passing a pointer might be slightly more efficient, in that it reduces a bit of copying.  Other than that, is there a reason why code should return pointers to error objects rather than error objects?

I did try looking through the FAQ, which asks "Should I define methods on values or pointers?" (although not "should I return values or pointers?"), and ends up by saying:

For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear.

Which makes me wonder if I'm missing some other reason for using pointers in this context.

Many thanks,

Brian.

Ian Lance Taylor

unread,
Nov 13, 2019, 12:54:32 PM11/13/19
to Brian Candler, golang-nuts
That code could have used a value rather than a pointer.

I didn't write it, but I would guess at two possible reasons for using
a pointer.

First, in practice most interfaces are used to hold pointer values.
This is not quite the same as whether a method should be defined on a
(non-pointer) value or a pointer. The question here is whether, when
converting a value to an interface type, one should convert a
(non-pointer) value or a pointer. In practice most people use
pointers. Not because non-pointer values don't work, but just because
it's a common style.

Second, and perhaps the reason the style has developed this way, the
current implementation of interfaces is that they always store pointer
values. If you store a non-pointer value in an interface, that value
is copied into memory, and a pointer to that memory is stored in the
interface. So in effect storing a pointer value writes out what the
implementation is doing anyhow. And then when you call a method, the
code is a tiny bit more efficient if it's a pointer method, since you
can just pass the pointer from the interface rather than copying the
value. These aren't particularly good reasons, since the efficiency
cost is likely unmeasurable outside of micro-benchmarks, but they
probably feed into the general style.

Ian

Brian Candler

unread,
Nov 13, 2019, 3:22:56 PM11/13/19
to golang-nuts
> the current implementation of interfaces is that they always store pointer
> values.  If you store a non-pointer value in an interface, that value
> is copied into memory, and a pointer to that memory is stored in the
> interface.

I don't understand where you say "that value is copied into memory".  If we're talking about a value that already exists, surely it just obtains a pointer to it, without any copying?

In other words: if you return a pointer value, then to convert it to an interface type it has to box it as [*T, ptr].  If you return a concrete object, then it has to take a pointer to that object, and box it as [T, ptr].

To my mind, the advantage of a concrete object is that it cannot be nil; if it's used as an interface value, there's only one possible way it can be nil. If you return a pointer to satisfy an interface then there are two ways it can be nil: a nil interface value, and a non-nil interface value containing a nil *T value.  It was a big source of confusion to me initially :-)

Still... if returning a pointer to an error is The Go Way, I guess I'll do that.  It would be nice if the documentation was more explicit about this.

Cheers,

Brian.

Ian Lance Taylor

unread,
Nov 13, 2019, 4:31:01 PM11/13/19
to Brian Candler, golang-nuts
On Wed, Nov 13, 2019 at 12:23 PM Brian Candler <b.ca...@pobox.com> wrote:
>
> > the current implementation of interfaces is that they always store pointer
> > values. If you store a non-pointer value in an interface, that value
> > is copied into memory, and a pointer to that memory is stored in the
> > interface.
>
> I don't understand where you say "that value is copied into memory". If we're talking about a value that already exists, surely it just obtains a pointer to it, without any copying?
>
> In other words: if you return a pointer value, then to convert it to an interface type it has to box it as [*T, ptr]. If you return a concrete object, then it has to take a pointer to that object, and box it as [T, ptr].

At the level at which we are talking, a value will typically be on the
stack or in registers, but a value converted to an interface type will
typically be on the heap. So that is the copy to which I referred:
from the stack to the heap. There are various exceptions and
conditions, but that is the most common case.


> To my mind, the advantage of a concrete object is that it cannot be nil; if it's used as an interface value, there's only one possible way it can be nil. If you return a pointer to satisfy an interface then there are two ways it can be nil: a nil interface value, and a non-nil interface value containing a nil *T value. It was a big source of confusion to me initially :-)
>
> Still... if returning a pointer to an error is The Go Way, I guess I'll do that. It would be nice if the documentation was more explicit about this.

I don't mean to say that it is The Go Way. I was only trying to
provide a plausible reason why the code in the blog post was written
that way. It would be absolutely fine to use a value rather than a
pointer; that would be just as much The Go Way.

Ian

Brian Candler

unread,
Nov 13, 2019, 4:53:34 PM11/13/19
to golang-nuts
Thanks.  It's much clearer now.

Cheers,

Brian.

Jake Montgomery

unread,
Nov 14, 2019, 11:55:44 AM11/14/19
to golang-nuts


On Wednesday, November 13, 2019 at 3:22:56 PM UTC-5, Brian Candler wrote:
> the current implementation of interfaces is that they always store pointer
> values.  If you store a non-pointer value in an interface, that value
> is copied into memory, and a pointer to that memory is stored in the
> interface.

I don't understand where you say "that value is copied into memory".  If we're talking about a value that already exists, surely it just obtains a pointer to it, without any copying?
 
I don't think it does, and that would not even be possible. If it did just use a pointer to the original value then modifications to the original would be reflected in the interface. If the interface holds a value, not a pointer, then that would be very unexpected. I think this code https://play.golang.org/p/9H4AFIGy-1I illustrates that a copy is indeed made when a interface holds a value.

Kevin

unread,
Nov 21, 2019, 10:33:49 AM11/21/19
to golang-nuts
seems I have found out why you must return a pointer but it has to be of type `error` return :/
Reply all
Reply to author
Forward
0 new messages