A pointer is an address value that points to the start of an area of memory. So you need to have a good knowledge of computer composition principles. Generally speaking, it is important to maintain a deep memory of what is told in this course and then accumulate it day by day to really reach an understanding.

The concept of a pointer in a high-level language is not fundamentally different from a pointer on a low-level interface. It’s just that because high-level languages often need to wrap the underlying physical structure to varying degrees, pointers have different manifestations in different high-level languages.

However, no matter how many differences there are, one thing is the same in high-level languages; if a pointer is allocated, then you need to care when it is unallocated. Of course, this tends to manifest itself more subtly in high-level languages with GC, where all you really care about is.

  1. whether the objects I create can be recycled reasonably
  2. my objects are not recycled reasonably and do not consume excessive CPU time
  3. my object will not be reclaimed late because of reference counting, which leads to insufficient memory
  4. Oops, Null Exception!

Whereas in a high-level language without GC, we might be concerned with

  1. how many objects did I create and did I destructure them?
  2. Do I create a lot of objects and destructure them frequently enough to cause heap space fragmentation problems?
  3. will the heap space fragmentation algorithm be too frequent and cause CPU overload
  4. will I get too much fragmentation and run out of memory, even though there is actually enough memory
  5. Am I using the right smart pointers?
  6. Null Exception

So I’m going to summarize how pointers work in Golang for once. Of course, it’s just a sloppy summary to sort out the various pieces of knowledge I’ve had for a long time.

You need to have sufficient experience in Golang coding, because the text will ignore the basics of Golang coding.

Cases

Should I use the structure or its pointer

You should read Go: Should I Use a Pointer instead of a Copy of my Struct? - by Vincent Blanchon - A Journey With Go - Medium, where they show that a copy of a struct may typically be 8 times faster than using a struct pointer. The reason for this is that using pointers causes struct variables to be placed in heap (after escape analysis), and GC is thus under more pressure.

In other words, the overhead of GC recycling a variable is much greater when using pointers, and large enough to affect efficiency. This is really counter-intuitive for C++ programmers.

go’s value passing is not as overhead intensive as expected, partly because copies are copied faster than expected.

Is that so?

However, as long as you are not sure whether to use pointers or not, use pointers in all cases. Basically, you don’t have to worry about GC pressure from using pointers.

Because unless you are making small objects frequently and discarding them immediately, most of the time the GC will not try to immediately recycle your pointers and the objects like structs they point to, so the so-called GC pressure is just illusory.

Imagine why we use structs? Because there is a block that contains different data, and we need to describe that block. So when we use a struct, we tend to need it to live longer, and obviously the GC must always turn a blind eye to it during its lifetime. So in general, you should always use pointers, and that’s the scenario that’s being talked about.

That is, whether struct should be immediately constructed as a pointer object depends on.

  1. the length of the survival cycle: long cycle always use pointers, short well feel free to good
  2. structure entity size: a structure of several hundred bytes or even tens of KB, you better use a pointer, or extreme cases of your stack space will even overflow, pay special attention to this case, it is not only difficult to detect, and is a security risk point.

In addition, struct also has some general knowledge

  1. You must use a pointer when you need to modify the data inside the structure question

    1
    2
    3
    4
    5
    6
    
    type xS struct {
    Status bool
    }
    
    func (x xS) SetStatusButWrong(b bool) { x.Status = b }
    func (x *xS) SetStatus(b bool) { x.Status = b }
    
  2. When a structure type contains a field member like sync.Mutex or an object in the sync package, always use the pointer construction method to prevent potential copy possibilities

    1
    2
    3
    4
    5
    6
    7
    
    type xinS struct {
    sync.RWMutex
    }
    
    func newXinS() *xinS {
    return &xinS{}
    }
    

Receiver

The receiver of a structure is recommended in preference to a pointer. See the previous case in the general knowledge section for the reason, because you often need to set (write) to it from outside the structure.

This pit/feature is well known. So this subsection won’t go into it again.

as method parameters

In general, when using structs as method arguments, always use the pointer form.

Even when you are using a struct object instead of its pointer construct form, if you pass it to a method, you should pass its pointer form by taking the address. Unless you explicitly want a copy of the struct to be passed to the method (perhaps you want to avoid the method causing side effects to the struct).

When using the pointer form for structure passing, you can avoid making a copy of the structure entity.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type xS struct {
  I8 int8
  I int
}

func A(){
  x := x{ 1, 2, }
  B(&x)
  println(x)
}

func B(x *xS) {
  x.I++
}

In the code example, the B method has two features.

  1. the ability to avoid unnecessary copies of the structure when passing the reference x
  2. the ability to modify the members of x and return this side effect to the caller

Once these two facts are clear, decide if you want to use pointer forms according to your actual needs. But your actual needs, as a rule of thumb, will say yes I do.

Basic Type

Basic types such as bool, int, float, and string usually do not need to use pointers.

For one thing, they are not expensive to copy from memory. Secondly, there are often language-specific optimizations for them, so you don’t have to expend your brain power on such minutiae.

It is worth noting that Primitive Datatypes are sometimes slightly ambiguous. This is especially true for string objects, and whether different languages have different design solutions and trade-off strategies for them.

In Golang, however, we can treat string as a primitive type. It is indeed one of the basic types, as defined by the Golang syntax specification.

map and so on

map, slice, and channel do not need to use pointers.

The reason is interesting, because they are themselves pointers: the

1
2
3
4
var m map[string]bool
if m == nil {
  println("an empty (or uninitialized) map is always nil")
}

So emptying an array looks like this.

1
2
3
4
5
var ss []int
// ... append some items into ss

// and reset ss
ss = nil

Others can imagine for themselves.

Use []T or []*T

Whenever possible, always use []*T.

Because the range operator always makes a copy of the item when iterating over range. So for []T, the contents of T cannot be modified during range iteration - or, if they are, they are immediately discarded and cannot be reflected in []T.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
type T struct {
  I8 int8
}

func Test(){
  var ta []*T
  for i := 0; i < 10; i++ {
    ta = append(ta, &{ int8(i) })
  }
  for _, t := range ta {
    t.I8++
  }
}

In the code example above, ta will eventually be incremented correctly, giving an array of 1..10.

Summary

What was presented earlier in this article is all nonsense, and all of the listed guideline entries are not true. There is really only one sentence.

Use pointers whenever possible, except for basic types, maps, slice, channels, etc.

As for whoever is saying that XXX gives an n-fold performance boost. Forget about them, performance improvement and escape analysis are all meaningless and you don’t even need to consider the extra factors. The hard truth is that optimization is not even your turn to think about it, and even if you always think about it, it doesn’t make you a good programmer, it’s probably the opposite.

Why?

Remember such accepted guidelines as.

  1. don’t optimize too early
  2. use profiling techniques to find optimization points after the product is finalized

As a programmer, writing the right code is a basic requirement. Until you can do that, don’t discuss performance in the same breath.