Skip to content

Golang | Pointers to slices, "slices sometimes altered when passed by value" #97

@atc0005

Description

@atc0005

nanxio.githbooks.io

So if you really want the function to change the content of a slice, you can pass the address of the slice

// https://nanxiao.gitbooks.io/golang-101-hacks/content/posts/pass-slice-as-a-function-argument.html
package main

import (
    "fmt"
)

func addValue(s *[]int) {
    *s = append(*s, 3)
    fmt.Printf("In addValue: s is %v\n", s)
}

func main() {
    s := []int{1, 2}
    fmt.Printf("In main, before addValue: s is %v\n", s)
    addValue(&s)
    fmt.Printf("In main, after addValue: s is %v\n", s)
}  

The result is like this:

In main, before addValue: s is [1 2]
In addValue: s is &[1 2 3]
In main, after addValue: s is [1 2 3]

medium.com/swlh

You can roughly imagine the implementation of the slice as:

type sliceHeader struct {
    Length        int
    Capacity      int
    ZerothElement *byte
}

Passing a slice to a function by value, all the fields are copied and only the data can be modified and accessed from outside through the copy of the pointer.

However, keep in mind that if the pointer is overwritten or modified (due to a copy, an assign, or an append) no change will be visible outside the function, moreover, no change of length or capacity will be visible to the initial function.

The answer to the questions, then, is simple, but hidden inside the implementation of the slice itself:

The pointer to a slice is indispensable when the function is going to modify the structure, the size, or the location in memory of the slice and every change should to be visible to those who call the function.

When we pass a slice to a function as an argument the values of the slice are passed by reference (since we pass a copy of the pointer), but all the metadata describing the slice itself are just copies.

We can modify the data of the slice in the literal function, however if the pointer to the data changes for any reason or the slice metadata is modified, the change can be partially or no visible at all to the outside function.

For example, if the slice gets allocated again, a new location of the memory is used; even if the values are the same, the slice points to a new location and therefore no modification of the values will be visible outside, since the slices are pointing to two different locations (the pointer in the slice copy got overwritten).

calhoun.io

As I mentioned before, when we call append a new slice is created. In the second quiz this new slice still pointed to the same array because it had enough capacity to add the new element, so the array wasn’t changed, but in this example we are adding three elements and our slice doesn’t have enough capacity. Instead, a new array is allocated and our updated slice points to it. When we finally start reversing elements in the slice it is no longer affecting our initial array, but it is instead operating on a completely different one.

References

PlayGround scratch notes:

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions