-1

Why in go editing slice inside functions don't apply length update?

Fast response: because a slice is only a reference to the original array

As you can see in the following example, I have a slice that will be modified in the values by external function but the append operation is totally ignored until you return the value from the function and re-assign this to the original slice.

Working to improve the examples, I've added function that pass by reference and make modification operations to increase comfort about the solutions

package main

import (
    "fmt"

)
var sli = make([]int,8)

func main() {

    fmt.Println("START STATUS_____")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))

    editSli(sli)
    fmt.Println("EDIT RESULT STATUS_____")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))


    sli = updateSli(sli)
    fmt.Println("UPDATE RESULT STATUS_____")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))

    sli = append(sli, 15, 16, 17)
    fmt.Println("DIRECT APPEND RESULT STATUS_____")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))

    sli[13] = 99
    fmt.Println("DIRECT ASSIGNMENT OF LAST ELEMENT (13°) STATUS_____")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))

    updateSliPointer(&sli)
    fmt.Println("\n*** Finally, passing the slice as a pointer will modify the sctructure: ")
    fmt.Println("slice",sli)
    fmt.Println("   cap:", cap(sli))
    fmt.Println("   len:", len(sli))
}

func editSli(sliOrigin []int) {
    sliOrigin[7] = 11
    sliOrigin = append(sliOrigin, 33, 34, 35) //don't work
    fmt.Println("\ninside the edit Slice function the sli is =", sliOrigin)
    fmt.Println("and the direct assignment of the last element (7°) ar right working.")
    fmt.Println("However, the slice append will not be applied to the original slice because this is an internal copy")
}

func updateSli(sliOrigin []int) []int{
    sliOrigin = append(sliOrigin, 12, 13, 14) //it works!
    fmt.Println("\ninside the update Slice function the sli is = ", sliOrigin)
    fmt.Println("and this append will be applied to the original one becouse of the return value of this function is a new slice")
    return sliOrigin
}

func updateSliPointer(sliOrigin *[]int) {
    //don't work *sliOrigin[7] = 11
    *sliOrigin = append(*sliOrigin, 100, 101, 102)
    fmt.Println("\ninside the UPDATE Slice POINTER function we have a sli pointer and we can modify directly the structures appending elements =", sliOrigin)
    fmt.Println("\nbut to access the values and modify it we have to create a local instance")
    var x = *sliOrigin
    x[4] = 9
}

Running this will produce:

START STATUS_____
slice [0 0 0 0 0 0 0 0]
    cap: 8
    len: 8

inside the edit Slice function the sli is = [0 0 0 0 0 0 0 11 33 34 35]
and the direct assignment of the last element (7°) ar right working.

However, the slice append will not be applied to the original slice because this is an internal copy
EDIT RESULT STATUS_____
    slice [0 0 0 0 0 0 0 11]
    cap: 8
    len: 8

inside the update Slice function the sli is =  [0 0 0 0 0 0 0 11 12 13 14]
and this append will be applied to the original one becouse of the return value of this function is a         new slice
UPDATE RESULT STATUS_____
    slice [0 0 0 0 0 0 0 11 12 13 14]
    cap: 16
    len: 11
DIRECT APPEND RESULT STATUS_____
    slice [0 0 0 0 0 0 0 11 12 13 14 15 16 17]
    cap: 16
    len: 14
DIRECT ASSIGNMENT OF LAST ELEMENT (13°) STATUS_____
    slice [0 0 0 0 0 0 0 11 12 13 14 15 16 99]
    cap: 16
    len: 14

inside the UPDATE Slice POINTER function we have a sli pointer and we can modify directly the         structures appending elements = &[0 0 0 0 0 0 0 11 12 13 14 15 16 99 100 101 102]

but to access the values and modify it we have to create a local instance

*** Finally, passing the slice as a pointer will modify the sctructure: 
    slice [0 0 0 0 9 0 0 11 12 13 14 15 16 99 100 101 102]
    cap: 32
    len: 17
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
bob
  • 21
  • 3

2 Answers2

8

You're fundamentally misunderstanding what a slice is. A slice is a structure with 3 fields: a pointer to the underlying array, the length of the data, and the capacity of the underlying array (relative to the pointer). When you pass a slice into a function, you're making a copy of that structure. It still references the underlying array, so changes that you make to the actual data will be reflected outside the scope of the function, but changes to the slice header itself (including the length of the data) won't, because it's a copy. If you need to modify the slice, rather than the underlying data, and have it reflected outside the function, pass in a pointer to the slice!

Recommended reading: https://blog.golang.org/go-slices-usage-and-internals

Kaedys
  • 9,600
  • 1
  • 33
  • 40
3

editSli doesn't work because you're changing the capacity of the local copy of the slice and not returning it. Basically, your function must return the new slice for the exact same reason that append must return the updated slice; because if it has to increase capacity, it may have to use a new underlying array, and therefor any old reference to the slice will be invalid.

Adrian
  • 42,911
  • 6
  • 107
  • 99