the contents of a slice argument can be modified by a function, but its header cannot. The length stored in the slice variable is not modified by the call to the function, since the function is passed a copy of the slice header, not the original. Thus if we want to write a function that modifies the header, we must return it as a result parameter, just as we have done here.
Indeed. So to answer the question in the topic title, itās idiomatic to have functions like slice = doSomethingWithSlice(slice) and less so to see doSomethingWithSlice(&slice). An exception is when you declare methods on a slice type (1) as those will by necessity have pointer-to-slice receivers if they intend to modify the slice;
type fibSlice []int
func (sp *fibSlice) append() {
s := *sp
l := len(s)
switch l {
case 0:
*sp = append(s, 0)
case 1:
*sp = append(s, 1)
default:
*sp = append(s, s[l-1]+s[l-2])
}
}
I avoided some of the incessant āstarringā by the s := *sp but it still gets annoying rather quickly. Itās usually neater to just declare a struct type containing the slice if Iām going to do to much of this.
It might be worth reading the internal details about what a slice actually is. When you pass a slice by value, your called function modifySlice gets a copy of the slice structure (a pointer and two ints), but that copy points to the same (possibly large) underlying array of storage as the slice in the calling code.
If you change the length of the slice inside the function, youāre just changing an int value in the slice structure, and that whole structure is thrown away when your function returns. But if you change one of the values in the underlying array, that change is potentially visible to all slices which point at the same underlying array, including the one in the calling code.
Even if doThing(&slice) were idiomatic Iād still avoid it, because functions with hidden side effects are generally a bad move. Itās cheap to pass slices by value, so you might as well do it.
So there are two things here, in the function modifySlice thereās two different access to the slice. The slice is, by definition, a pointer to an underlying array.
This code i[0] = "3" takes the position 0 in i and set it to "3", which is the original slice, since even when it doesnāt seem like itās a pointer, it still is.
Hereās the issue: the append function makes a checkā¦ Quoting the docs:
If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated.
So when you do i = append(i, "4") youāre essentially saying: "add ā4ā to this slice, but since the original āiā slice has reached its maximum capacity, create a new one, add ā4ā and then set it to āiā. Since āiā only exists within the modifySlice() function, you created a new slice that youāll never return or use anymore.
Each slice has a length and a capacity. āThe length of a slice is the number of elements it contains. The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.ā So when you declared var s = []string{"1", "2", "3"}, you created a slice with length of 3 and capacity of 3. Changing one value that is already in the slice wonāt create another slice, but appending to a slice that doesnāt have the capacity to hold more items will create a new one.