Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should I use the & sign on structs?

Tags:

go

In the gotour, there is a section: struct literals.

package main

import "fmt"

type Vertex struct {
    X, Y int
}

var (
   v1 = Vertex{1, 2}  // has type Vertex
   v2 = Vertex{X: 1}  // Y:0 is implicit
   v3 = Vertex{}      // X:0 and Y:0
   p  = &Vertex{1, 2} // has type *Vertex
)

func main() {
   fmt.Println(v1, p, v2, v3)
}

What's the difference between a struct with an ampersand and the one without? I know that the ones with ampersands point to the same reference, but why should I use them over the regular ones?

 var p = &Vertex{} // why should I use this
 var c = Vertex{} // over this
like image 639
Tomy Avatar asked Jan 07 '18 08:01

Tomy


1 Answers

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
        
like image 106
YenForYang Avatar answered Oct 09 '22 22:10

YenForYang