Akavall
Akavall

Reputation: 86168

Behavior of a pointer to an element of `slice` after the `slice` had been appended to

I am wondering what is the behavior of a pointer to an element of slice after the slice had been appended to, for example:

package main

import "fmt"

func main() {
    my_slice := []int {3}

    silly_ptr := &my_slice[0]
    // Do we know that silly_ptr points to value equal 3
    // all the time? (If we don't explicitly change it).

    fmt.Printf("%p\n", silly_ptr)
    fmt.Println(*silly_ptr)

    for i := 0; i < 10; i++ {
        my_slice = append(my_slice, i)
    }

    silly_ptr_2 := &my_slice[0]

    fmt.Printf("%p\n", silly_ptr_2)
    fmt.Println(*silly_ptr_2)
}

Produces: (no surprises)

0xc20800a200
3
0xc20805a000
3

I know that when appending to dynamic array, at certain points we have repopulate the entire array, and therefore memory address of the original array elements is not reliable. To the best of my knowledge similar code is valid in c++, but silly_ptr could be pointing to anything. rust does not allow mutating a vector if it is being borrowed, so the above logic would not compile.

But what about Go? I know that by escape analysis it is valid to return a pointer to a local variable, the variable would be just created on the heap for you. My intuition tells me that the same logic applies in the above case. The memory location where silly_ptr is pointing to will not be repopulated, and hence will always store 3 (if we don't explictly change it). Is this right?

Upvotes: 1

Views: 71

Answers (1)

peterSO
peterSO

Reputation: 166569

No, it will not always store 3.

Go has memory management. As long as there is an active pointer to an underlying array for a slice, the underlying array is pinned, it will not be garbage collected. If you have a pointer to an element of an underlying array, you can change the value of the element. For example,

package main

import (
    "fmt"
)

func pin() *int {
    s := []int{3}
    fmt.Println(&s[0])
    a := &s[0]
    s = append(s, 7)
    fmt.Println(&s[0])
    return a
}

func main() {
    a := pin()
    fmt.Println(a, *a)
    *a = 42
    fmt.Println(a, *a)
}

Output:

0xc82000a340
0xc82000a360
0xc82000a340 3
0xc82000a340 42

A slice descriptor contains a pointer to an underlying array so you can see something similar with a slice. For example,

package main

import (
    "fmt"
)

func pin() []int {
    s := []int{3}
    fmt.Println(&s[0])
    d := s
    s = append(s, 7)
    fmt.Println(&s[0])
    return d
}

func main() {
    d := pin()
    fmt.Println(&d[0], d)
    d[0] = 42
    fmt.Println(&d[0], d)
}

Output:

0xc82000a340
0xc82000a360
0xc82000a340 [3]
0xc82000a340 [42]

Upvotes: 1

Related Questions