thwd
thwd

Reputation: 24848

What are pointers to pointers good for?

In the Go programming language; how can pointers to pointers become useful?

(Why are they not illegal if they are not really useful?)

Upvotes: 18

Views: 14680

Answers (6)

StasVo
StasVo

Reputation: 944

For example with ** possible to use this code:

var target *string
setValue(&target, "пример строки")

func setValue(target **string, value string) {
    if len(value) > 0 {
        *target = &value
    }
}

as alternative to:

var target *string
target = setValue("пример строки")

func setValue(value string) *string{
    if len(value) > 0 {
        return &value
    }
    return nil
}

Upvotes: 1

zzzz
zzzz

Reputation: 91369

Here is an example involving ** right in the standard library:

https://github.com/golang/go/blob/cfe2ab42e764d2eea3a3339aac1eaff97520baa0/src/encoding/gob/decoder.go#L20

Upvotes: 3

Robert Smart
Robert Smart

Reputation: 111

Linus Torvalds recently mentioned how pointers to pointers lead to code with good taste (in C). See (among others) Brian Barto's blog post.

Upvotes: 4

OCyril
OCyril

Reputation: 3115

When you pass a pointer to function, function gets a copy of it. So assigning new value to the given pointer will not result in assigning it to the original one:

type Smartphone struct {
    name string
}

type Geek struct {
    smartphone *Smartphone
}

func replaceByNG(s **Smartphone) {
    *s = &Smartphone{"Galaxy Nexus"}
}

func replaceByIPhone(s *Smartphone) {
    s = &Smartphone{"IPhone 4S"}
}

func main() {
    geek := Geek{&Smartphone{"Nexus S"}}
    println(geek.smartphone.name)

    replaceByIPhone(geek.smartphone)
    println(geek.smartphone.name)

    replaceByNG(&geek.smartphone)
    println(geek.smartphone.name)
}

The output is:

Nexus S
Nexus S
Galaxy Nexus

Upvotes: 26

tux21b
tux21b

Reputation: 94759

In C pointers to pointers are quite common. For example:

  • more dimensional arrays (for example an array of strings, char** argv might be the most prominent example here)
  • pointers as output parameters

In Go however, pointers to pointers are quite rare. Instead of accessing arrays by a pointer, there is a slice type (which also stores a pointer internally). So, you still might get the same kind of indirection by using a slice of slices in Go, but you normally won't see something like **int here.

The second example however might still apply to Go programs. Let's say you have a function which should be able to change a pointer passed as parameter. In that case, you will have to pass a pointer to that pointer, so that you can change the original pointer. That's extremely common in C, because functions can only return one value (which is often some kind of error code) and if you want to return an additional pointer, you will have to use a pointer to that pointer as output parameter. A function in Go however, can return multiple values, so the occurrences of pointers to pointers are also rare. But they might still be useful and might lead to a nicer API in some cases.

For example, the atomic.StorePointer function might be one of those rare but well hidden use-cases for pointers to pointers in the standard library.

Upvotes: 7

user811773
user811773

Reputation:

The usefulness of any data type depends on the problem being solved and on the method used to solve the problem. If a data type does not fit the problem, it simply does not fit the problem - and there is nothing more to it.

The Go programming language (as well as most other programming languages) is based on simple rules that the programmer can use to build new data types. Some of these rules are:

  • *T: create a new data type that is a pointer to T
  • [10]T: an array of Ts
  • struct { t T; u U ... }: a structure which contains a T as a component
  • ...

The programmer can create complex data types by composing these simple rules. The total number of possible data types exceeds the number of useful data types. Clearly, there exist (and have to exist) data types which aren't useful at all. This is just a natural consequence of the fact that the rules for building new data types are simple.

The type **T falls into the category of types which are less probable to appear in a program. The fact that it is possible to write *****T doesn't imply that such a type has to be immensely useful.


And finally, the answer to your question:

The type **T usually appears in contexts where we want to redirect users of a value of type T to another value of type T, but for some reason we do not have access to all users of the value or finding the users would cost too much time:

  1. We do not want to copy values of type T (for some reason)
  2. We want all users of a value of type T to access the value via a pointer
  3. We want to quickly redirect all users of a particular value of type T to another value

In such a situation, using **T is natural because it allows us to implement the 3rd step in O(1):

type User_of_T struct {
  Value **T
}

// Redirect all users of a particular value of type T
// to another value of type T.
func (u *User_of_T) Redirect(t *T) {
  *(u.Value) = t
}

Upvotes: 36

Related Questions