-1

In most golang codebases I look, people are using types by reference:

type Foo struct {}
myFoo := &Foo{}

I usually take the opposite approach, passing everything as copy and only pass by reference when I want to perform something destructive on the value, which allows me to easily spot destructive functions (and which is fairly rare).

But seeing how references are commonplace, I guess it's not just a matter of taste. I get there's a cost in duplicating values, is it that much of a game changer? Or are there other reasons why references are preferred?

It would be great if someone could point me to an article or documentation about why references are preferred.

Thanks!

kik
  • 7,867
  • 2
  • 31
  • 32
  • 2
    Possible duplicate of [Pointers vs. values in parameters and return values](http://stackoverflow.com/questions/23542989/pointers-vs-values-in-parameters-and-return-values); and [Why should constructor of Go return address?](http://stackoverflow.com/questions/31932822/why-should-constructor-of-go-return-address), and [returning value vs pointer in Go constructor](http://stackoverflow.com/questions/32208363/returning-value-vs-pointer-in-go-constructor). – icza Oct 21 '16 at 11:16
  • Hi @icza, thanks for pointing those out, they did not appeared as suggestions as I typed. The first one indeed seems to address the same question, but the selected answer 1/ refer to "guidelines" and "good practice" without clearly stating the reasons why (but a lot of useful info, still!) and 2/ quite seems to recommend actually avoiding using &Type{} by default :) I guess it could be useful to clearly state the reasons in favor of this pattern here. – kik Oct 21 '16 at 11:25
  • That's not only about speed, Olivier. Consider you constructed a bunch of `Foo`s, and placed these values into several distinct containers (say, slices) copying them by value. This means each container now has a copy of each of the source value, and changing each of them in any of these containers will not have any effect on the others. Sometimes it's okay, sometimes it's not. In each case you should think what *semantics* you want to put to the values of a type you're creating and how do you intend to use values of that type. – kostix Oct 21 '16 at 12:19
  • Indeed :) In that example, does dereferencing upon variable initialization offers anything more than using `append( myslice, &myItem )`, though? – kik Oct 21 '16 at 12:26

1 Answers1

1

Go is pass by value. I try to use references like in your example as much as possible to remove the mental process of thinking about not making duplicates of objects. Go is mostly meant for networking & scaling, which makes performance a priority. Obvious downside of this is as you say, receiving methods can destroy the object that the pointer points to.

Otherwise there is no rule as to which you should use. Both are quite ok.

Also, somewhat related to the question, from the Go docs: Pointers vs. Values

Teoman Soygul
  • 25,584
  • 6
  • 69
  • 80
  • 1
    Hi Teoman, thanks for answer! I'll make a benchmark to try to measure the actual cost of duplication. Thanks for the doc, too, but I was actually wondering about docs around the `&Type{}` pattern :) I'll clarify the question. – kik Oct 21 '16 at 11:34
  • 1
    Ok, here is a benchmark : https://gist.github.com/oelmekki/d7e93170b00278d73ccea052a2ee7ed7 That's about 500ms difference for 1B function calls. So I guess the conclusion is that it's worth using references for extremely intensive computation (like networking you mentioned), but not that much for smaller apps. – kik Oct 21 '16 at 11:51
  • Just to be sure, I ran the benchmark again with setting an actual value in the `Name` field, this does not change the results. – kik Oct 21 '16 at 11:53
  • 1
    @OlivierElMekki: dereferencing a pointer can be expensive, and a CPU can copy a lot of data in the same time. Structures smaller than a cache-line or 2 (which most are) aren't going to benefit performance wise from pointers. The logic of the program, sharing the address for mutability or copying the value, is going to dictate which to use. – JimB Oct 21 '16 at 12:47
  • @JimB Can you show an example where this happens? I've edited my benchmark to include accessing a field value, and it's still 500ms better when passing a reference: https://gist.github.com/oelmekki/c39a1347455badfbbff268da27c3ccba – kik Oct 21 '16 at 14:08
  • 1
    @OlivierElMekki: benchmarks are hard to get right, especially microbenchmarks that need to take into account data locality, CPU specific cache behavior and branch prediction. In your example, the compiler could choose to completely remove either of those calls, and you could be timing something else entirely since you're not doing any real work. Using the current compiler and benchmarking tool, both those calls will take exactly the same amount of time, probably because they are optimized out – JimB Oct 21 '16 at 14:34