Why should I use the & sign on structs?

The comments pretty much spell it out:

   v1 = Vertex{1, 2}  // has type Vertex
   p  = &Vertex{1, 2} // has type *Vertex

As in many other languages, & takes the address of the identifier following it. This is useful when you want a pointer rather than a value.

If you need to understand more about pointers in programming, you could start with this for go, or even the wikipedia page.


It's true that p (&Vertex{}) has type *Vertex and that c (Vertex{}) has type Vertex.

However, I don't believe that statement really answers the question of why one would choose one over the other. It'd be kind of like answering the question "why use planes over cars" with something like "planes are winged, cars aren't." (Obviously we all know what wings are, but you get the point).

But it also wouldn't be very helpful to simply say "go learn about pointers" (though I think it is a really good idea to so nonetheless).


How you choose basically boils down to the following.

  • Realize that &Vertex{} and Vertex{} are fundamentally initialized in the same way.

    • There might be some low-level memory allocation differences (i.e. stack vs heap), but you really should just let the compiler worry about these details
  • What makes one more useful and performant than the other is determined by how they are used in the program.

  • "Do I want a pointer to the struct (p), or just the struct (c)?"

    • Note that you can get a pointer using the & operator (e.g. &c); you can dereference a pointer to get the value using * (e.g. *p)
      • So depending on what you choose, you may end up doing a lot of *p or &c
      • Bottom-line: create what you will use; if you don't need a pointer, don't make one (this will help more with "optimizations" in the long run).

  • Should I use Vertex{} or &Vertex{}?
    • For the Vertex given in your example, I'd choose Vertex{} to a get a simple value object.
    • Some reasons:
      • Vertex in the example is pretty small in terms of size. Copying is cheap.
      • Garbage collection is simplified, garbage creation may be mitigated by the compiler
      • Pointers can get tricky and add unnecessary cognitive load to the programmer (i.e. gets harder to maintain)
      • Pointers aren't something you want if you ever get into concurrency (i.e. it's unsafe)
      • Vertex doesn't really contain anything worth mutating (just return/create a new Vertex when needed).
    • Some reasons why you'd want &Struct{}
      • If Struct has a member caching some state that needs to be changed inside the original object itself.
      • Struct is huge, and you've done enough profiling to determine that the cost of copying is significant.
        • As an aside: you should try to keep your structs small, it's just good practice.
      • You find yourself making copies by accident (this is a bit of a stretch):
         v := Struct{}
        v2 := v  // Copy happens
        
         v := &Struct{}
        v2 := v  // Only a pointer is copied
        

Tags:

Go