In Go there are various ways to return a struct
value or slice thereof. For individual ones I've seen:
type MyStruct struct { Val int } func myfunc() MyStruct { return MyStruct{Val: 1} } func myfunc() *MyStruct { return &MyStruct{} } func myfunc(s *MyStruct) { s.Val = 1 }
I understand the differences between these. The first returns a copy of the struct, the second a pointer to the struct value created within the function, the third expects an existing struct to be passed in and overrides the value.
I've seen all of these patterns be used in various contexts, I'm wondering what the best practices are regarding these. When would you use which? For instance, the first one could be ok for small structs (because the overhead is minimal), the second for bigger ones. And the third if you want to be extremely memory efficient, because you can easily reuse a single struct instance between calls. Are there any best practices for when to use which?
Similarly, the same question regarding slices:
func myfunc() []MyStruct { return []MyStruct{ MyStruct{Val: 1} } } func myfunc() []*MyStruct { return []MyStruct{ &MyStruct{Val: 1} } } func myfunc(s *[]MyStruct) { *s = []MyStruct{ MyStruct{Val: 1} } } func myfunc(s *[]*MyStruct) { *s = []MyStruct{ &MyStruct{Val: 1} } }
Again: what are best practices here. I know slices are always pointers, so returning a pointer to a slice isn't useful. However, should I return a slice of struct values, a slice of pointers to structs, should I pass in a pointer to a slice as argument (a pattern used in the Go App Engine API)?
You need to pass a pointer to a function when you need to modify the elements of an array. In C, you can't pass an array or return an array from a function. Answer for your second question is, in C function argument are passed by value.
The difference between pass-by-pointer and pass-by-value is that modifications made to arguments passed in by pointer in the called function have effect in the calling function, whereas modifications made to arguments passed in by value in the called function can not affect the calling function.
We can pass pointers to the function as well as return pointer from a function. But it is not recommended to return the address of a local variable outside the function as it goes out of scope after function returns.
The return statement should not return a pointer that has the address of a local variable ( sum ) because, as soon as the function exits, all local variables are destroyed and your pointer will be pointing to someplace in the memory that you no longer own.
tl;dr:
One case where you should often use a pointer:
Some situations where you don't need pointers:
Code review guidelines suggest passing small structs like type Point struct { latitude, longitude float64 }
, and maybe even things a bit bigger, as values, unless the function you're calling needs to be able to modify them in place.
bytes.Replace
takes 10 words' worth of args (three slices and an int
). You can find situations where copying even large structs turns out a performance win, but the rule of thumb is not to.For slices, you don't need to pass a pointer to change elements of the array. io.Reader.Read(p []byte)
changes the bytes of p
, for instance. It's arguably a special case of "treat little structs like values," since internally you're passing around a little structure called a slice header (see Russ Cox (rsc)'s explanation). Similarly, you don't need a pointer to modify a map or communicate on a channel.
For slices you'll reslice (change the start/length/capacity of), built-in functions like append
accept a slice value and return a new one. I'd imitate that; it avoids aliasing, returning a new slice helps call attention to the fact that a new array might be allocated, and it's familiar to callers.
interface{}
parameter.Maps, channels, strings, and function and interface values, like slices, are internally references or structures that contain references already, so if you're just trying to avoid getting the underlying data copied, you don't need to pass pointers to them. (rsc wrote a separate post on how interface values are stored).
flag.StringVar
takes a *string
for that reason, for example.Where you use pointers:
Consider whether your function should be a method on whichever struct you need a pointer to. People expect a lot of methods on x
to modify x
, so making the modified struct the receiver may help to minimize surprise. There are guidelines on when receivers should be pointers.
Functions that have effects on their non-receiver params should make that clear in the godoc, or better yet, the godoc and the name (like reader.WriteTo(writer)
).
You mention accepting a pointer to avoid allocations by allowing reuse; changing APIs for the sake of memory reuse is an optimization I'd delay until it's clear the allocations have a nontrivial cost, and then I'd look for a way that doesn't force the trickier API on all users:
bytes.Buffer
.Reset()
method to put an object back in a blank state, like some stdlib types offer. Users who don't care or can't save an allocation don't have to call it.existingUser.LoadFromJSON(json []byte) error
could be wrapped by NewUserFromJSON(json []byte) (*User, error)
. Again, it pushes the choice between laziness and pinching allocations to the individual caller.sync.Pool
handle some details. If a particular allocation creates a lot of memory pressure, you're confident you know when the alloc is no longer used, and you don't have a better optimization available, sync.Pool
can help. (CloudFlare published a useful (pre-sync.Pool
) blog post about recycling.)Finally, on whether your slices should be of pointers: slices of values can be useful, and save you allocations and cache misses. There can be blockers:
NewFoo() *Foo
rather than let Go initialize with the zero value.append
copies items when it grows the underlying array. Pointers you got before the append
point to the wrong place after, copying can be slower for huge structs, and for e.g. sync.Mutex
copying isn't allowed. Insert/delete in the middle and sorting similarly move items around.Broadly, value slices can make sense if either you get all of your items in place up front and don't move them (e.g., no more append
s after initial setup), or if you do keep moving them around but you're sure that's OK (no/careful use of pointers to items, items are small enough to copy efficiently, etc.). Sometimes you have to think about or measure the specifics of your situation, but that's a rough guide.
Three main reasons when you would want to use method receivers as pointers:
"First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer."
"Second is the consideration of efficiency. If the receiver is large, a big struct for instance, it will be much cheaper to use a pointer receiver."
"Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used"
Reference : https://golang.org/doc/faq#methods_on_values_or_pointers
Edit : Another important thing is to know the actual "type" that you are sending to function. The type can either be a 'value type' or 'reference type'.
Even as slices and maps acts as references, we might want to pass them as pointers in scenarios like changing the length of the slice in the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With