The below snippets of code appeared in the lab on pointers.
main.go gives an output of
[10 20 30]
[5 15 25]
again.go gives an output of
[10 20 30]
[10 20 30]
Could someone explain to me the reason for the difference in output? I thought slices in golang are passed by reference and would therefore give the same output as that for main.go
In main.gothis is happening. The key point is in the second paragraph where it says “In this case, no new slice is created”, so the arr slice is effectively passed by reference.
An unbounded slice like []int will always be a reference, since the compiler cannot know in advance how much stack to allocate for the function arguments.
In again.go the modify() function expects an explicit array of exactly 3 integers, and so is passed by value as the compiler knows to reserve stack space for exactly 3 integers.
We can rewrite it like this, since we know the type is a [3]int and use pointers to achieve the same result as main.go
func main() {
arr := [3]int{10, 20, 30}
fmt.Println(arr)
modify(&arr)
fmt.Println(arr)
}
func modify(numbers *[3]int) {
for i := range numbers {
numbers[i] -= 5
}
}
A slice is actually represented internally as a struct containing 3 items. We don’t see this unless we dip into the realms of the unsafe package (advanced stuff). This struct contains 2 integers (length and capacity) and a pointer to some memory which is the values in your slice.
When you have a slice as a function argument, this hidden struct is passed by value, however because the slice contents are referred to by a pointer in this struct, then those values are not passed by value, only the pointer to them is. Hence if you modify the slice values in a function, the caller sees these changes.
There aren’t really many use cases for arrays, since slices are much more convenient.