notexOPENOverview
Notex/Languages/Pointers Indicate Mutable Parameters
04.06LESSON 06 OF 20 · 3 MIN READ

Pointers Indicate Mutable Parameters

Always free to read

As you’ve already seen, Go constants provide names for literal expressions that can be calculated at compile time. Go has no mechanism to declare that other kinds of values are immutable. Modern software engineering embraces immutability. MIT’s course on software construction sums up the reasons: “Immutable types are safer from bugs, easier to understand, and more ready for change. Mutability makes it harder to understand what your program is doing, and much harder to enforce contracts.”

Go’s lack of immutable declarations might seem problematic, but the ability to choose between value and pointer parameter types addresses the issue. The course materials go on to explain that “using mutable objects is just fine if you are using them entirely locally within a method, and with only one reference to the object.” Rather than declaring that some variables and parameters are immutable, Go developers use pointers to indicate that a parameter is mutable.

Since Go is a call-by-value language, the values passed to functions are copies. For nonpointer types like primitives, structs, and arrays, this means that the called function cannot modify the original. Since the called function has a copy of the original data, the original data’s immutability is guaranteed.

However, if a pointer is passed to a function, the function gets a copy of the pointer that still points to the original data. This means that the called function can modify the original data.

This has a couple of implications. The first is that, when you pass a nil pointer to a function, you cannot make the value non-nil. You can reassign the value only if a value was already assigned to the pointer. While this can be confusing at first, it makes sense. Since the memory location was passed to the function through call-by-value, you can’t change the memory address, any more than you could change the value of an int parameter. You can demonstrate this with the following program:

func failedUpdate(g *int) {
    x := 10
    g = &x
}

func main() {
    var f *int // f is nil
    failedUpdate(f)
    fmt.Println(f) // prints nil
}

The flow through this code is shown in Figure 5-6.

Failing To Update a nil Pointer

Figure 5-6. Failing to update a nil pointer

You’ll start with a nil variable f in main. When you call failedUpdate, copy the value of f, which is nil, into the parameter named g. This means that g is also set to nil. Then declare a new variable x within failedUpdate with the value 10. Next, change g in failedUpdate to point to x. This does not change the f in main. When you exit failedUpdate and return to main, f is still nil.

The second implication of copying a pointer is that, if you want the value assigned to a pointer parameter to still be there when you exit the function, you must dereference the pointer and set the value. If you change the pointer, you have changed the copy, not the original. Dereferencing puts the new value in the memory location to which both the original and the copy point. Here’s a short program that shows how this works:

func failedUpdate(px *int) {
    x2 := 20
    px = &x2
}

func update(px *int) {
    *px = 20
}

func main() {
    x := 10
    failedUpdate(&x)
    fmt.Println(x) // prints 10
    update(&x)
    fmt.Println(x) // prints 20
}

The flow through this code is shown in Figure 5-7.

In this example, you start with x in main set to 10. When you call failedUpdate, you copy the address of x into the parameter px. Next, you declare x2 in failedUpdate, set to 20. You then point px in failedUpdate to the address of x2. When you return to main, the value of x is unchanged. When you call update, you copy the address of x into px again. However, this time you change the value of what px in update points to: the variable x in main. When you return to main, x has been changed.

The Wrong Way and The Right Way To Update a Pointer

Figure 5-7. The wrong way and the right way to update a pointer

Progress is saved on this device.
KEEP EXPLORING

One note leads to another.

Back to the library →
⌕
Search across the entire library ⌘ K