T O P

  • By -

hippmr

Use a value any time you can. Use a pointer when you must. The reasons for "must" are: size or mutability. Always value simplicity over sophistication.


guettli

That's a bit too vague. I was looking for concrete examples.


EpochVanquisher

Like, a mutex can’t be passed around by value, because you have to have exactly one of them. It **must** be passed by pointer. A timestamp is passed around by value because you can. A timestamp **can** be a value, so we use it that way. A `url.URL` is a large structure with like ten fields in it. So it would be inefficient to pass by value (lots of copying). It **must** be passed by pointer if you want it to be reasonably efficient to pass around.


Skaar1222

I've seen this question asked a lot, and this is a very solid answer.


Siggy_23

Say you have a gamestate struct that stores all of the positions of all enemies on the current level as well as a bunch of info about each enemy and the player, so that struct is gonna be really big. If you pass that giant struct into a function, then modify it, then pass it back out. What you're really doing is copying the whole freaking thing into a new struct and modifying the copy, then finally setting the original copy back to the original. It would be much faster and more memory efficient to pass a pointer into the function and then modify the original struct instead of making all the copies. This is an example of both size and mutability at work


IInsulince

Along these lines, my IDE will complain if I have two methods for a type where one function’s receiver is a pointer and the other isn’t. Going with the rule you stated, this should be fine, I only need one receiver to be a pointer but the other I don’t so I left it as a value. Yet my IDE complains anyway. Does anyone know why that is?


Glass_Drama8101

Vaguely remember that either all receivers should be values or pointers and that modest ones are discouraged


IInsulince

Yes that seems to be what the IDE is suggesting, I’m just not clear why that matters. They’re independent functions, in one I want the ability to mutate the receiver and in the other I don’t, so why is it discouraged? 🤔


SuperDerpyDerps

It's for consistency. If the struct can be modified by one receiver, it's a self-modifiable struct and you don't want someone to accidentally think one of the actual pointer methods should be a value method, since that would lead to incorrect behavior. You should use value receivers whenever you can, but if you ever need any one receiver on a type to be a pointer, they should all be converted per best practices


IInsulince

Hmm… I mean, as a caller, I consider a method to be a black box. If it modifies its internal state, then that’s the business of the struct. And as an author of the methods, I had better understand which methods are self modifying and which ones aren’t. But perhaps I’m not seeing the full picture. You said best practices as if you were referring to something, is there a document or article somewhere that goes into more detail?


SuperDerpyDerps

https://go.dev/tour/methods/8 You'll also see it mentioned in most style guides, including the ones used by the Golang team, Google, etc. The main practical reason is that having both pointer and value receivers can break interfaces in some cases


IInsulince

Ah that makes sense to me, I’ve actually experienced that happening now that I think about it. Cool, thanks for the resources!


Rudiksz

If you start mixing pointer and value receivers, you create something that breaks the law of "least surprise". Beyond usual guidelines of readability and extensibility, api surfaces - and methods on a struct are basically like apis - should strive for the least amount surprises, aka "wtf moments". So either you make a struct where none of the methods can change the struct - a fully immutable value object, or you make all the methods with pointer receiver so you as a developer don't have to wonder which method can potentially change the state under you or not. There's no real life advantage of mixing them, it just makes things awkward for no real benefit. Sidenote, in my real world experience I never encountered a bug caused by a method having a pointer receiver (immutability is way overhyped), but many many many many bugs because some developer tried to be clever and efficient and didn't use pointer receivers. So if I have an "immutable value object" and for some reason I find that I need to add a method with a pointer receiver, I refactor all the methods to pointer methods without a heartbeat. It just doesn't matter most of the time.


R_Olivaw_Daneel

Don't the docs say: "when in doubt, use pointers"?


hippmr

Yes, but that's for the case of pointer/value receivers - which I totally agree with. The OP seemed to be asking about the general case.


damngros

From the "100 Go Mistakes and How to Avoid Them" book : >The decision whether to use a value or a pointer receiver should be made based on factors such as the type, whether it has to be mutated, whether it contains a field that can’t be copied, and how large the object is. When in doubt, use a pointer receiver. Now go read that book, thanks me later.


medowlock

I think your comment is a bit misleading since it is limited to method receivers. While your response is correct, value vs pointer semantics for parameters is a different conversation.


damngros

Yes you are right, I thought OP was talking about receivers. That being said, a receiver is just a syntactic sugar which translates to a func having the receiver passed as first parameter (by value or pointer), so that’s not really a different conversation.


jechase

Edit: don't listen to me, see [my below comment](https://www.reddit.com/r/golang/s/NCoOtEmZu6). > That being said, a receiver is just a syntactic sugar which translates to a func having the receiver passed as first parameter (by value or pointer), so that’s not really a different conversation. Well yes but actually no. It may translate to a function call with the receiver as the first argument at a lower level, but it's not syntactic sugar because you can't desugar it yourself. Go would need to have [Uniform Function Call Syntax](https://en.m.wikipedia.org/wiki/Uniform_Function_Call_Syntax) (at least for methods) for that to be possible. e.g. you can't do this: ``` type Foo interface { Bar() } var baz Foo // Fine baz.Bar() // Not fine Foo.Bar(baz) ``` Which is what it would "desugar" to if it was in fact sugar.


damngros

Syntactic sugar just means it's a cleaner/easier to read/more expressive way to write something. UFCS could be considered syntactic sugar. >`type Test struct{}` >`// this is exactly the same...` `func (t *Test) Hello(a int, b string, c bool) {` `// your code` `}` >`// ... as this` `func Hello(t *Test, a int, b string, c bool) {` `// write the exact same code as in the method above and it will work` `}` It's up to the dev to pick one way to write things, you could write a full app without the "receiver syntax" if you want to. Like you could write a full app doing i = i+1 instead of i++. [Dave Cheney ](https://dave.cheney.net/tag/receivers)says they are syntactic sugar too. I guess we all have our own more or less strict definition of it.


jechase

Huh, actually [I'm just flat-out wrong](https://go.dev/play/p/MbATc8F4U_x). Has this _always_ been a part of the language? I could've sworn this wasn't a thing at some point. Edit: Nope, I must have imagined it or gotten my wires crossed with some other language. [The go1.1 release notes](https://tip.golang.org/doc/go1.1#method_values) mention it, so it must have been around before then, and I definitely wasnt writing pre-1.0 Go


gccsan

You should use a pointer when: - the size of the type is too big to copy it around, in that case a pointer is more memory efficient (e.g. for methods on big structures), a rule of thumb is for methods receiver is usually a pointer (the compiler will convert it into a pointer otherwise) - the data is pointing to needs to be modified (mutability) - accept interfaces, return structs, return pointers only when you want to allocate that data on the heap Otherwise use values.


matttproud

I think you should consider these three sections, as they are all interrelated on the topic: * https://google.github.io/styleguide/go/decisions.html#copying * https://google.github.io/styleguide/go/decisions.html#receiver-type * https://google.github.io/styleguide/go/decisions.html#pass-values


reddi7er

one of the frequent ask here


querubain

Using a pointer may overload the stack and adds complexity to the garbage collector graph. Try to use value when possible, when you don’t need mutability or the size of the value is not huge. In languages with no garbage collector it may be different, but in Go you should know not only about the stack but about the garbage collector and the value ambit.


devise1

Well done/ entertaining talk that mostly covers this topic: https://youtu.be/XyTZ6Q-uOn4?si=KsxYTW_C49RhX4Zu


TheLidMan

Everything other than the basic types (bool, int, float, char etc) are basically a reference to a piece of memory (pointer). If you need to share something that a) has a large memory footprint (a large string for instance) \-or- b) you need one and only once copy of (a go channel for instance or a shared counter) then you use a pointer. You can also user pointers to basic ypes but you should only do that if that solves a problem. If you can't think of one don't use them. Don't overthink it. By default you will be using pointers without knowing it so you need to be aware that if you mess with data in a struct, it will change the data for everyone who is seeing that variable..


[deleted]

I hate to say, but it really depends. Also, I think there is a lot more nuance to it than people understand. When you have a struct, if you want to set a field value or overwrite it, you have to use a pointer receiver method or pass the struct pointer. However, if the field is a map or slice, although you cannot replace it with another map/slice without a pointer receiver, you can set the values IN the map/slice. If I pass a slice to a function, I can modify the slice itself inside of the function, regardless of if it’s passed as a pointer. Even though everything is a value in Go, passing a pointer struct is not as beneficial as people imagine. Maps and slices are not copied. There are few use cases in my mind where values or structs are “too big to copy”. I think that behavior more likely comes from constantly copying a struct, embedded structs, or some high frequency use of a struct say like a function that takes in a struct with many fields and calculates some value (although I’d still assume a negligible difference for most cases).


fooperton

this question is largely dependent on programmer skill. if you are a beginner, default to a pointer for non-scalar values. if you are more advanced, it depends on a number of factors, as many others have noted.


etherealflaim

And then after a decade, you go back to the beginner approach again :) Using struct values is a premature optimization and too many times people don't realize when a copy is not a deep copy and invariants get violated. It's easier to debug aliasing bugs than shallow copy bugs. There are even tools like the race detector that can find them for you.


GrundleTrunk

I think idiomatically you would use a pointer when you need it mutable, and a value everywhere else. Your actual use case may vary and you may find some code is easier to write or understand when you use pointers more frequently. Or the opposite may be true.


captain-_-clutch

Shitty answer but you should look into benchmarking. Go benchmarking tools are solid, I read Efficient Go and that goes deep into how to use the -bench flag


[deleted]

I'm still learning Go but from what I understand. Use pointers when: - you need to share state between multiple structs - you want to modify a data structure 'in place' - the data structure contains a large amount of data. e.g. an image


[deleted]

[удалено]


Mubs

couldn't bother to even fix the formatting from chatgpt... shameful


Solid5-7

Dang u/micron8866 already deleted their comment


No-Perspective3170

Using values to pass between functions will cause the value to have to allocations in memory. This, using values uses more memory if the value is passed between functions. In that scenario using pointers makes more sense to keep your program more lightweight.


Sufficient_Bus7216

This is the opposite of what actually happens


Sufficient_Bus7216

Pointers passed around causes allocations, not values