1

This is pointers to pointers

package main

import "fmt"

func main() {
    var num int

    fmt.Println(&num) //  0x...0
    makePointer(&num)
}

func makePointer(firstPointer *int) {
    fmt.Println(firstPointer)  //  0x...0
    fmt.Println(&firstPointer) //  0x...1

    makePointerToAPointer(&firstPointer)
}

func makePointerToAPointer(secondPointer **int) {
    fmt.Println(secondPointer)  //  0x...1
    fmt.Println(&secondPointer) //  0x...2

}

When would you actually use this? You can properly come up with something where it would be easier to do something else, but that is not what I asking about. I really want to know where in production you would use this?

J. Eng
  • 680
  • 1
  • 9
  • 14

3 Answers3

5

Pointers to pointers make sense in function parameters sometimes; not **int probably, but a pointer to a pointer to some struct, where you want the function to be able to change what object a variable points to, not just to change the contents of the struct. For example, there are a few functions in the internals of the Go compiler that take a **Node (see cmd/compile/internal/gc/racewalk.go).

I've also written a couple of functions myself that take a **html.Node; they operate on an HTML page that may or may not have already been parsed into a tree of *html.Nodes, and they may or may not need to parse the page—but if they do, I want to keep the parsed tree around so that I don't have to parse it again. These are in github.com/andybalholm/redwood/prune.go.

They are much more common in languages that do not have multiple return values, since they can be used as a way to return an additional value that is a pointer. Many Objective-C methods take an NSError** as their last parameter so that they can optionally return an NSError*.

andybalholm
  • 15,395
  • 3
  • 37
  • 41
4

The goal to pass a pointer to something is if there is need to modify the pointed value. (We also use pointers to avoid copying large data structures when passing, but that is just for optimization.)

Like in this example:

func main() {
    var i int
    fmt.Println(i)
    inc(&i)
    fmt.Println(i)
}

func inc(i *int) {
    *i++
}

Output is the expected (try it on the Go Playground):

0
1

If parameter of inc() would receive an int only, it could only modify the copy and not the original value, and so the caller would not observe the changed value.

Same goes with pointer to pointer to something. We use pointer to pointer to something, if we need to modify the pointed value, that is the pointed pointer. Like in this example:

func main() {
    var i *int
    fmt.Println(i)
    alloc(&i, 1)
    fmt.Println(i, *i)

    setToNil(&i)
    fmt.Println(i)
}

func alloc(i **int, initial int) {
    *i = new(int)
    **i = initial
}

func setToNil(i **int) {
    *i = nil
}

Output (try it on the Go Playground):

<nil>
0x1040a130 1
<nil>

The reason why pointer to pointer is not really used is because modifying a pointed value can be substituted by returning the value, and assigning it at the caller:

func main() {
    var i *int
    fmt.Println(i)
    i = alloc(1)
    fmt.Println(i, *i)

    i = setToNil()
    fmt.Println(i)
}

func alloc(initial int) *int {
    i := new(int)
    *i = initial
    return i
}

func setToNil() *int {
    return nil // Nothing to do here, assignment happens at the caller!
}

Output is the same (address might be different) (try it on the Go Playground):

<nil>
0x1040a130 1
<nil>

This variant is easier to read and maintain, so this is clearly the favored and wide-spread alternative to functions having to modify a pointer value.

In languages where functions and methods can only have 1 return value, it usually requires additional "work" if the function also wants to return other values besides the pointer, e.g. a wrapper is to be created to accommodate the multiple return values. But since Go supports multiple return values, need for pointer to pointer basically drops to zero as it can be substituted with returning the pointer that would be set to the pointed pointer; and it does not require additional work and does not make code less readable.

This is a very similar case to the builtin append() function: it appends values to a slice. And since the slice value changes (its length increases, also the pointer in it may also change if a new backing array needs to be allocated), append() returns the new slice value which you need to assign (if you want to keep the new slice).

See this related question where a pointer to pointer is proposed (but also returning a pointer is also viable / preferred): Golang: Can the pointer in a struct pointer method be reassigned to another instance?

icza
  • 389,944
  • 63
  • 907
  • 827
  • What about a goroutine changing a value. You can't work with return values there. You could use a channel to pipe the value change out but that might be overkill. Of course you might need to use a mutex for access to the variable. – TehSphinX Jul 25 '17 at 08:48
  • @TehSphinX I still think returning the pointer is easier to read and maintain even when running it in a goroutine. To make that convenient, I would simply use an anonymous function where you can deal with the return values, e.g. `go func() { i = alloc(1) }()` – icza Jul 25 '17 at 08:59
0

In the same way a pointer to a value lets you have many references to the same value for a consistent view of the value when it changes, a pointer to a pointer lets you have many references to the same reference for a consistent view of the pointer when it changes to point to a different location in memory.

I can't say I've ever seen it used in practice in Go that I can think of.

Adrian
  • 42,911
  • 6
  • 107
  • 99
  • I know, it is simply that I can't really see an example where this would be more readable than doing something else. I really want a concrete example where it makes sense to use point to pointers instead of other options. – J. Eng Jul 24 '17 at 19:47
  • There may not be a strong use case, which might be why you never see it used in the wild. Why are you searching for a reason to use something no one ever uses? – Adrian Jul 24 '17 at 19:51
  • Pointers to pointers are about as useful as pointers to interfaces. Both are rare, but sometimes they are the right tool for the job. – Milo Christiansen Jul 24 '17 at 19:56
  • 2
    This is C code, but the principles should transfer: Linux Torvalds's go to example of pointers to pointers https://grisha.org/blog/2013/04/02/linus-on-understanding-pointers/ – 3Doubloons Jul 24 '17 at 19:59
  • It properly has its place in programming, but because people don't know about it and where to use it they might add some extra layer of complexity or do something in a harder way. That is why I want to know about it, – J. Eng Jul 24 '17 at 20:00
  • I am new to low-level languages, do you have a good link to an article about "pointers to interfaces"? – J. Eng Jul 24 '17 at 20:02
  • @3Doubloons I have just tried to read the example, but It is really just going over my head. I can see that it uses pointers to pointers though. Do you have other examples please? – J. Eng Jul 24 '17 at 20:18
  • @TehSphinX, I rejected your edit because it doesn't really illustrate what I was trying to say very well. It may be good as *another* example of a situation where pointers to pointers are needed though. – andybalholm Jul 24 '17 at 21:43
  • 1
    @J.Eng The key in the last chunk of code is `pp = &entry->next` and `*pp = entry->next`. `pp` will always be the address of either the "head" pointer or the "next" pointer. If it's a "next" pointer, you're always updating the "next" pointer of the previous entry. If you're still puzzled, try adding a `listEntry *prev = entry;` line before the `pp = &entry->next;` line, and change things to `pp = &prev->next;` and `entry = prev->next;` and see if it makes sense. –  Jul 25 '17 at 01:28
  • @ChronoKitsune The thing is that golang is the first low-level programming language. I have used node.js and happy with it as a backend. So when you come with C where there is `->` I don't know what you are saying, sorry. I will thumbs you up for taking the effort to help, but I do not get it. And I don't have C install on my mac, so I can't change it. I could go and download it, but the answer to this question is not worth downloading C. – J. Eng Jul 25 '17 at 16:38