126

I know that pointers in Go allow mutation of a function's arguments, but wouldn't it have been simpler if they adopted just references (with appropriate const or mutable qualifiers). Now we have pointers and for some built-in types like maps and channels implicit pass by reference.

Am I missing something or are pointers in Go just an unnecessary complication?

hannson
  • 4,465
  • 8
  • 38
  • 46
anon
  • 1,287
  • 2
  • 9
  • 3
  • 1
    Here's a question that may help clarify: http://stackoverflow.com/questions/795160/java-is-never-pass-by-reference-right-right/795186#795186 There is a difference between passing references by value and passing truly by reference. – R. Martinho Fernandes Dec 07 '09 at 22:59
  • 1
    Note: the question is about Java, but it applies here as well. – R. Martinho Fernandes Dec 07 '09 at 23:00
  • 2
    "and for some built-in types like maps and channels implicit pass by reference." No, everything is pass-by-value in Go. Some types are (informally described as) reference types, since they have internal mutable state. – newacct Dec 29 '12 at 02:47
  • 1
    The problem with this question is that "references" are not a single thing with well defined properties. The term "references" is very vague. We can see in the answers how many people read different things into the word "references". So this question should elaborate on exactly what differences there are between Go pointers and the references the question has in mind. – mtraceur Jul 10 '20 at 18:46

5 Answers5

40

Pointers are usefull for several reasons. Pointers allow control over memory layout (affects efficiency of CPU cache). In Go we can define a structure where all the members are in contiguous memory:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

In this case the Point structures are embedded within the LineSegment struct. But you can't always embed data directly. If you want to support structures such as binary trees or linked list, then you need to support some kind of pointer.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python etc doesn't have this problem because it does not allow you to embed composite types, so there is no need to syntactically differentiate between embedding and pointing.

Issues with Swift/C# structs solved with Go pointers

A possible alternative to accomplish the same is to differentiate between struct and class as C# and Swift does. But this does have limitations. While you can usually specify that a function takes a struct as an inout parameter to avoid copying the struct, it doesn't allow you to store references (pointers) to structs. This means you can never treat a struct as a reference type when you find that useful e.g. to create a pool allocator (see below).

Custom Memory Allocator

Using pointers you can also create your own pool allocator (this is very simplified with lots of checks removed to just show the principle):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Swap two values

Pointers also allows you to implement swap. That is swapping the values of two variables:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Conclusion

Java has never been able to fully replace C++ for systems programming at places such as Google, in part because performance can not be tuned to the same extend due to the lack of ability to control memory layout and usage (cache misses affect performance significantly). Go has aimed to replace C++ in many areas and thus needs to support pointers.

Erik Engheim
  • 8,182
  • 4
  • 36
  • 51
  • 12
    C# allows to pass structs by reference. See "ref" and "out" keywords. – olegz Jan 17 '15 at 15:36
  • 1
    Okay so it is like Swift. I will think about a way to update my example. – Erik Engheim Jan 19 '15 at 13:28
  • I don't understand the first point, can't left and right just be TreeNode? And don't most typed languages allow for recursive types / interfaces, without the use of pointers? – Andy Ray Apr 25 '21 at 23:26
  • No it cannot be TreeNode as space is set aside inside the struct for each of the members. If you set aside space for a whole TreeNode inside a TreeNode you end up with a sort of Infinite recursive definition taking infinite space. You got to be able to terminate the tree by setting left and/or right to null. A TreeNode cannot be null. Only pointer such as *TreeNode can be null. – Erik Engheim Jan 24 '22 at 14:22
  • Most languages allow recursive structure because they actually use pointers. Java and many others are simply being silly by not calling their references pointers. They are pointers. The issue with most other languages is that they almost only support pointers and no value types. This means they don’t have typical pointer operations such as taking an address or dereferencing. They cannot do this because they have no value type to do it on other than primitive values like float and int. – Erik Engheim Jan 24 '22 at 14:26
35

I really like example taken from https://www.golang-book.com/books/intro/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

as contrasted with

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}
max23_
  • 6,531
  • 4
  • 22
  • 36
Piotr Kochański
  • 21,862
  • 7
  • 70
  • 77
  • 63
    The question was "why do we have pointers **instead of references**" and I don't understand why this example wouldn't work with references. – AndreKR Jun 03 '16 at 20:29
  • @AndreKR Because we can choose whether to pass by reference or to pass by value. There are some instances where both can be desirable. – Jax Dec 31 '16 at 07:44
  • 17
    @DJMethaneMan It's "pointers vs. references", not "pointers vs. pass-by-value"! – AndreKR Dec 31 '16 at 14:13
  • As a side comment, pass-by-reference was added in C# 2.0 via the "ref" keyword. Of course pointers are still more convenient in certain cases, because we can have pointer to pointer to pointer... – robbie fan Dec 18 '17 at 02:53
  • 4
    I don't get how Go is supposed to be one of the easiest popular languages and yet they contain such a "feature"... It's confusing and seems unnecessary, at least to the people pointing this out here. – Akito Aug 23 '20 at 18:23
34

Go is designed to be a terse, minimalist language. It therefore started with just values and pointers. Later, by necessity, some reference types (slices, maps, and channels) were added.


The Go Programming Language : Language Design FAQ : Why are maps, slices, and channels references while arrays are values?

"There's a lot of history on that topic. Early on, maps and channels were syntactically pointers and it was impossible to declare or use a non-pointer instance. Also, we struggled with how arrays should work. Eventually we decided that the strict separation of pointers and values made the language harder to use. Introducing reference types, including slices to handle the reference form of arrays, resolved these issues. Reference types add some regrettable complexity to the language but they have a large effect on usability: Go became a more productive, comfortable language when they were introduced."


Fast compilation is a major design goal of the Go programming language; that has its costs. One of the casualties appears to be the ability to mark variables (except for basic compile time constants) and parameters as immutable. It's been requested, but turned down.


golang-nuts : go language. Some feedback and doubts.

"Adding const to the type system forces it to appear everywhere, and forces one to remove it everywhere if something changes. While there may be some benefit to marking objects immutable in some way, we don't think a const type qualifier is to way to go."

James Curbo
  • 148
  • 9
peterSO
  • 158,998
  • 31
  • 281
  • 276
  • FWIW, "reference types" in Go are also reassignable. They're more like implicit pointers? – Matt Joiner May 24 '12 at 13:11
  • 1
    They are just special syntax for structs that contain a pointer (and length, capacity, ...). – mk12 Jul 15 '13 at 16:32
  • could you add definition about "what is a reference". I am not sure what to think about `Later, by necessity, some reference types (slices, maps, and channels) were added`. Aint they just value with attached pointers ? –  Sep 26 '21 at 09:15
29

References cannot be reassigned, while pointers can. This alone makes pointers useful in many situations where references could not be used.

zildjohn01
  • 11,339
  • 6
  • 52
  • 58
1

Rather than answering it in the context of “Go”, I would answer this question in the context of any language (e.g. C, C++, Go) which implements the concept of "pointers"; and the same reasoning can be applied to “Go” as well.

There are typically two memory sections where the memory allocation takes place: the Heap Memory and the Stack Memory (let’s not include “global section/memory” as it would go out of context).

Heap Memory: this is what most of the languages make use of: be it Java, C#, Python… But it comes with a penalty called the “Garbage Collection” which is a direct performance hit.

Stack Memory: variables can be allocated in the stack memory in languages like C, C++, Go, Java. Stack memory doesn’t require garbage collection; hence it is a performant alternative to the heap memory.

But there is a problem: when we allocate an object in the heap memory, we get back a “Reference” which can be passed to “multiple methods/functions” and it is through the reference, “multiple methods/functions” can read/update the same object(allocated in the heap memory) directly. Sadly, the same is not true for the stack memory; as we know whenever a stack variable is passed to a method/function, it is “passed by value”(e.g. Java) provided you have the “concept of pointers”(as in the case of C, C++, Go).

Here is where pointers come into picture. Pointes let “multiple methods/functions” read/update the data which is placed in the stack memory.

In a nutshell, “pointers” allow the use of “stack memory” instead of the heap memory in order to process variables/structures/objects by “multiple methods/functions”; hence, avoiding performance hit caused by the garbage collection mechanism.

Another reason for introducing pointers in Go could be: Go is ought to be an "Efficient System Programming Language" just like C, C++, Rust etc. and work smoothly with the system calls provided by the underlying Operating System as many of the system call APIs have pointers in their prototype.

One may argue that it can done by introducing a pointer-free layer on top of the system call interface. Yes, it can be done but having pointers would be like acting very close to the system call layer which is trait of a good System Programming Language.

Shariq Ehsan
  • 186
  • 1
  • 8