DEV Community

Cover image for Go Slices: Sharing is Caring, Until You Run Out of Room
mahdi
mahdi

Posted on

Go Slices: Sharing is Caring, Until You Run Out of Room

In this post, we will demonstrate the relationship between a slice and its underlying array. To explain this concept, we'll use a code example and walk through it step-by-step.

func main() {
    var a [3]int = [3]int{1, 2, 3}
    b := a[0:1]
    c := a[0:2]

    b = append(b, 4)                 // grows b, mutates a
    fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 3]
    fmt.Printf("b[%p]= %[1]v\n", b)  // b[0xc0000121f8]= [1, 4]

    c = append(c, 5)                 // grows c, mutates a
    fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
    fmt.Printf("c[%p]= %[1]v\n", c)  // c[0xc0000121f8]= [1, 4, 5]

    c = append(c, 6)                 // because cap c is 3, this operation forces to reallocate c with doubling cap
    fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
    fmt.Printf("c[%p]= %[1]v\n", c)  // c[0xc0000145d0]= [1, 4, 5, 6]
    fmt.Println("cap c: ", cap(c))   // cap c: 6

    c[0] = 9                         // mutates a different array!
    fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
    fmt.Printf("c[%p]= %[1]v\n", c)  // c[0xc0000145d0]= [9, 4, 5, 6]

}
Enter fullscreen mode Exit fullscreen mode

1-initialization:

So when we define a, b and c we some something like this image.

As we can see a is an array and b, c is slices that a is their underlying array.

2- b = append(b, 4)

• b has a length of 1 and a capacity of 3. There is room to add elements.
• No reallocation is needed. The append operation adds the value 4 into the next available spot in the underlying array a.
• This overwrites a[1].
• a is mutated.

3- c = append(c, 5)

• c has a length of 2 and a capacity of 3. There is still room.
• No reallocation is needed. The append adds 5 into the next spot in the underlying array a.
• This overwrites a[2].
• a is mutated again.

4- c = append(c, 6)

• c now has a length of 3 and a capacity of 3. There is no more room.
• To add 6, append must perform a reallocation.
• It creates a new, larger array (with a new capacity, typically doubled to 6), copies the elements [1, 4, 5] over, and then appends 6.
• The slice c is updated to point to this new array. It no longer shares data with a.
• a is not affected.

5- c[0] = 9

• This modifies the first element of the slice c.
• Since c now points to its own separate, underlying array, this change only affects c.
• a remains completely untouched.

code output

a[0xc0000121f8]= [1 4 3]
b[0xc0000121f8]= [1 4]
a[0xc0000121f8]= [1 4 5]
c[0xc0000121f8]= [1 4 5]
a[0xc0000121f8]= [1 4 5]
c[0xc0000145d0]= [1 4 5 6]
cap c:  6
a[0xc0000121f8]= [1 4 5]
c[0xc0000145d0]= [9 4 5 6]
Enter fullscreen mode Exit fullscreen mode

The memory addresses shown in the output (like 0xc0000121f8) are specific to a single execution of the program. When you run the code on your machine, the operating system will allocate memory at different addresses.

Resources

Go Class: 10 Slices in Detail

Top comments (0)