0

In Go we can choose to make something a pointer or not. We can also support multiple return types. A common signature on functions or methods is:

type MyType struct{}

// Value Return Type
func (m MyType) FindOne(key string)(MyType, error) {...}

// Pointer Return Type
func (m MyType) FindOne(key string)(*MyType, error) {...}

In reviewing the repos on Github (non-exhaustive) for signatures like the above, it seems to be preferred to use the pointer style return type when something can also return an error. Is that the general consensus?

Few Examples (again non-exhaustive I have definitely not seen everything there is to see):

https://github.com/etcd-io/etcd/blob/9c5426830b1b8728af14e069acfdc2a64dea768c/clientv3/kv.go#L48-L54

https://github.com/go-redis/redis/blob/d19aba07b47683ef19378c4a4d43959672b7cec8/cluster.go#L329

https://github.com/bradfitz/gomemcache/blob/master/memcache/memcache.go#L317

In thinking about it, and here's where my question lies, I would think the pointer style would be preferred on calls where a result might not exist:

service := MyType{}
result, err := service.FindOne("not-found-key")

In this way result can be nil and only the err has a value. Effectively preventing anyone from operating result. Whereas in the value type return, if nothing is found, we would still need to return an empty MyType{}. Assuming we are good Go citizens, we would naturally error check things always. Nothing is perfect and things slip by, so in the case where someone were to do something like this:

service := MyType{}
result, _ := service.FindOne("not-found-key")

// do stuff with `result` because of course it will ALWAYS succeed
// we don't need to error check (until we do)

The execution will continue even though we are now in a potentially unknown state. Where as is we tried to do things to that pointer return value, since it's nil, it would panic and we'd know about it right away.

This has additional implications as well in the design of our MyType service. To keep things consistent, if we introduced at FindMany method:

type MyType struct{}

func (m MyType) FindOne(key string)(*MyType, error) {...}
func (m MyType) FindMany(key string)([]*MyType, error) {...}

We would probably benefit from keeping the Pointer return types consistent so callers don't have to think too much as it's always the same either a Pointer or Value of MyType.

3
  • What are the advantages and disadvantages of each approach? That's the question you have to ask yourself. Then you make a decision. I don't know if Go works like other languages, but in most languages the reason you use a pointer or reference is because (in general) you want to keep things on the heap, not on the stack. Jan 30, 2020 at 21:43
  • Found a nice reference explaining at least in 2017, how Go deals with things: segment.com/blog/… Feb 4, 2020 at 0:32
  • The pointer return type also maps better to a "no result found" instead of an empty result if you choose value semantics. With your FindMany() receiver the zero value of a slice is nil. Nov 18, 2021 at 8:19

1 Answer 1

1

I don't think it particularly matters which you use, at least not relating to error handling. The whole point of returning an err along with a result is so that you can test for an error before accessing the return result. So ultimately it shouldn't matter if your return a valid , because if you are properly checking the error result first, an invalid would never be referenced.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.