5

I tried to add an element to my slice inside a function. I can change the element of the slice but cannot add a new element to it. Since slices act like reference why can't I change it?

Below is the code I have tried:

package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 3}
    change(a)
    fmt.Println(a)
}
func change(a []int) {
    a[0] = 4
    a = append(a, 5)
}
Himanshu
  • 12,071
  • 7
  • 46
  • 61
Mahdi
  • 377
  • 1
  • 4
  • 8
  • 1
    To do that you need to return a in the change function and assign it to a in main ` a=change(a)` or pass a by reference `func change(a *[]int)` Structs passed by value in go. – jesusnoseq Sep 29 '18 at 06:05
  • Thanks, Worked, does is means that I just can update the fields using slice reference? – Mahdi Sep 29 '18 at 06:08
  • You can update the elements in slices without need to pass the slice by reference but you can't modify the slice itself (length and capacity) To better understanding of how slice works https://blog.golang.org/go-slices-usage-and-internals – jesusnoseq Sep 29 '18 at 06:14

1 Answers1

11

Slice are pointers to underlying array. It is described in Golang:

Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn't copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to.

you are passing a copy of the slice not the original slice. Return the value after appending to the slice and then assign it to the original slice as

package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 3}
    a = change(a)
    fmt.Println(a)
}

func change(a []int) []int{
    a = append(a, 5)
    return a
}

Playground Example

Or you can pass a pointer to slice of int but it is not recommended since slice it self is a pointer to bootstrap array.

package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 3}
    change(&a)
    fmt.Println(a)
}

func change(a *[]int){
    *a = append(*a, 5)
}

Note: Everything in Golang is pass by value.

One thing to be considered is even if you are returning the updated slice and assigning to the same value, its original len and cap will change, which will lead to a new underlying array of different len. Try to print the length and cap before and after changing the slice to see the difference.

fmt.Println(len(a), cap(a))

The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer).

Since the underlying array will check you can check it using reflect and unsafe for fetching the underlying array which is going to be different if cap of a slice change after appending data which is your case.

package main

import (
    "fmt"
    "reflect"
    "unsafe"
)

func main() {
    a := []int{1, 2, 3}
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(&a))
    data := *(*[3]int)(unsafe.Pointer(hdr.Data))
    fmt.Println(data)
    a = change(a)
    hdr = (*reflect.SliceHeader)(unsafe.Pointer(&a))
    newData := *(*[4]int)(unsafe.Pointer(hdr.Data))
    fmt.Println(newData)
}

func change(a []int) []int {
    a = append(a, 5)
    return a
}

Playground Example

This is the best part of slices that you need to worry about its capacity when appending data more than its capacity, since it will point to a new array allocated in the memory of bigger length.

Himanshu
  • 12,071
  • 7
  • 46
  • 61