Cannot append to slice inside a function [duplicate] - pointers

This question already has an answer here:
Why does append() modify the provided slice? (See example)
(1 answer)
Closed 4 years ago.
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)
}

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.

Related

Appending to a slice with enough capacity using value receiver

can someone help me understand what happens here?
package main
import (
"fmt"
)
func appendString(slice []string, newString string) {
slice = append(slice, newString)
}
func main() {
slice := make([]string, 0, 1)
appendString(slice, "a")
fmt.Println(slice)
}
I know about the slice header and the need to use a pointer receiver. But here, as the underlying array has enough capacity I would expect append to work anyways (just adding the new value to the underlying array and the original [copied] header working as expected)
What is wrong with my assumptions?
Let's add a final print statement to see the result:
slice := make([]string, 0, 1)
fmt.Println(cap(slice))
appendString(slice, "a")
fmt.Println(slice)
And the output will be (try it on the Go Playground):
1
[]
Which is correct. One could expect the output to be:
1
[a]
The reason why this is not the case is because even though a new backing array will not be allocated, the slice header in the slice variable inside main() is not changed, it will still hold length = 0. Only the slice header stored in the slice local variable inside appendString() (the parameter) is changed, but this variable is independent from main's slice.
If you were to reslice main's slice, you will see that the backing array does contain the new string:
slice := make([]string, 0, 1)
fmt.Println(cap(slice))
appendString(slice, "a")
fmt.Println(slice)
slice = slice[:1]
fmt.Println(slice)
Now output will be (try it on the Go Playground):
1
[]
[a]
This is why the builtin append() has to return the new slice: because even if no new backing array is needed, the slice header (which contains the length) will have to be changed (increased) if more than 0 elements are appended.
This is why appendString() should also return the new slice:
func appendString(slice []string, newString string) []string {
slice = append(slice, newString)
return slice
}
Or short:
func appendString(slice []string, newString string) []string {
return append(slice, newString)
}
Which you have to reassign where you use it:
slice := make([]string, 0, 1)
fmt.Println(cap(slice))
slice = appendString(slice, "a")
fmt.Println(slice)
And then you get the expected outcome right away (try it on the Go Playground):
1
[a]

Why can't you append a pointer to an array?

package main
import "fmt"
type Circle struct {
x string
}
func main() {
circle := Circle{x: "blah"}
results := make([]*Circle, 1)
results = append(results, &circle)
fmt.Printf("Here: %s\n", results[0].x)
}
If I change the line results = append(results, &circle) to results[0] = &circle works fine. Couldn't find any reason as to why that would be.
You can, it's just that you are appending to the slice, which means that the element you add is in results[1], and results[0] is a nil pointer (the default value for a pointer).
You could do results := make([]*Circle, 0, 1) to give it a capacity of 1 but a length of zero, or you could do results := []*Circle{} (most compact), or you could simply keep the version where you assign to results[0], since it works just fine.
You can, but you are using make with length 1, so the [0] index is already set (to a nil Circle...)
Changing your results[0].x to results[1].x works, but without using make works as expected:
package main
import "fmt"
type Circle struct {
x string
}
func main() {
circle := Circle{x: "blah"}
results := []*Circle{} // initialize empty slice of Circle pointers
results = append(results, &circle)
fmt.Printf("Here: %s\n", results[0].x)
}
Run in playground: https://play.golang.org/p/7vMOfAXvgI

Go loop pointer changes [duplicate]

This question already has answers here:
Using Pointers in a for loop
(2 answers)
How to understand this behavior of goroutine?
(2 answers)
Golang Reusing Memory Address Copying from slice?
(2 answers)
Register multiple routes using range for loop slices/map
(1 answer)
Convert slice of string to slice of pointer to string
(2 answers)
Closed 9 months ago.
I am using a for range loop in Go to iterate through a slice of structs.
In each loop, I a pointer to the current item to a variable.
I am confused why the pointer changes value in the next loop.
For example this code:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for _, i := range l {
fmt.Println("begin", p)
p = &i
fmt.Println("end", p)
}
}
I would expect to produce:
begin <nil>
end &{1}
begin &{1}
end &{2}
But actually does:
begin <nil>
end &{1}
begin &{2}
end &{2}
For reference, in my actual code, I am checking for a condition during the loop, and returning the current item and previous one. So I am trying to save a pointer to it, so that in the next iteration it will have access to the previous as well.
The problem is that you're taking the address of the loop/range variable and not the address of the item in slice. However, you're just making a lot of unnecessary work for yourself. For one, why don't you use the i, v := range or better yet i, _ := and then you can do i-1 to get the previous item? Secondly, even if you want it saved in a pointer, still use this syntax and then assign p = &l[i] so you have the address of the item in the slice rather than the address of the loop/range variable.
People are way too eager to use for/each style constructs when it's obviously better to work with the index... If you want index-1 on every iteration, using the index should be your go to way of doing that.
Building off Tim's comment, it seems like you can copy the value on each loop, instead of the pointer, and dereference it after.
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p t
var i t
for _, i = range l {
fmt.Println("begin", &p)
p = i
fmt.Println("end", &p)
}
}
Another option is to get the pointer to the current item by using the index:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for index, _ := range l {
fmt.Println("begin", p)
p = &l[index]
fmt.Println("end", p)
}
}

why can't I use reflection to take the address of a slice?

How come this works:
slice := make([]string, 0, 10)
sliceptr := &slice
this too:
sliceptr := &[]string{"foo","bar","baz"}
But this doesn't:
sliceaddrval := reflect.ValueOf([]string{"foo","bar","baz"}).Addr()
It panics with: reflect.Value.Addr of unaddressable value
EDIT: Overall what I'm trying to do is take a struct that is of an unknown type, make a slice of structs of that type and return a pointer to it (I'm using github.com/jmoiron/modl which requires a pointer to slice to populate with results from a SQL query).
reflect.Value takes an interface{}, and an interface{} to a value can't be used to change the original. Otherwise, you could end up with code changing data in your struct when you didn't even intend to pass it a pointer. (Or, in this case, changing the length of a slice that was passed by value.) So if you take the address you'd have to do it before the ValueOf.
To make a pointer to a slice that you can to pass to a package that will append to it (like modl or Google App Engine GetMulti), you'd use something like http://play.golang.org/p/1ZXsqjrqa3, copied here:
package main
import (
"fmt"
"reflect"
)
type row struct { i, j int }
func main() {
aRow := row{}
valueType := reflect.ValueOf(aRow).Type()
slicePtrVal := reflect.New(reflect.SliceOf(valueType))
slicePtrIface := slicePtrVal.Interface()
getQueryResults(slicePtrIface)
fmt.Println(slicePtrIface)
}
// standing in for `modl` or whatever populates the slice
func getQueryResults(slicePtr interface{}) {
sPtr := slicePtr.(*[]row)
(*sPtr) = append((*sPtr), row{1,3})
}
Appending to a slice in a reflect.Value yourself takes another few lines of reflect, but it sounds like the package you're working with takes care of that part for you. For general info, code to do the append is at http://play.golang.org/p/m3-xFYc6ON and below:
package main
import (
"fmt"
"reflect"
)
type row struct { i, j int }
func main() {
aRow := row{}
// make a pointer to an empty slice
rowType := reflect.ValueOf(aRow).Type()
slicePtrVal := reflect.New(reflect.SliceOf(rowType))
slicePtrIface := slicePtrVal.Interface()
// append a zero row to it
rowVal := reflect.Zero(rowType)
sliceVal := reflect.Indirect(slicePtrVal)
sliceVal.Set(reflect.Append(sliceVal, rowVal))
fmt.Println(slicePtrIface)
}

Slice juggling in golang

To be short, here is a deal:
http://play.golang.org/p/ePiZcFfPZP
If I use commented lines, everything works, but there is no
any control on allocation sizes (cap), so the slices,
if I got it correct, realloc every time they exceed their limit
and moreover, they start with zero capacity.
Passing a reference of newSlice in setSlice() don't work too.
So, I need ideomatic, elegant, go-ish method to do the job.
Thanks in advance, at least for attention and your time.
UPD:
solution was to make SLICE and STASH *[]byte typed
and make assigns to them like:
var slicePtr *[]byte
tmp := make([]byte, 256)
slicePtr = &tmp // Tmp is needed because we can't take adress of make() rval.
For example,
package main
import "fmt"
var SLICE, STASH []byte
func init() {
SLICE = make([]byte, 0, 5)
}
func setSlice(slice []byte) {
STASH = SLICE
SLICE = slice
}
func restoreSlice() {
SLICE = STASH
}
func appendToSlice(parts ...byte) []byte {
SLICE = append(SLICE, parts...)
return SLICE
}
func main() {
appendToSlice('f', 'o', 'o')
fmt.Printf("Everything is fine: {'%s'}\n", SLICE)
newSlice := make([]byte, 0, 5)
setSlice(newSlice)
newSlice = appendToSlice('b', 'a', 'r')
fmt.Printf("Bar? No! {'%s'}\n", newSlice) // <- I need "bar" appear in newSlice.
fmt.Printf("Bar is here: {'%s'}\n", SLICE)
restoreSlice()
fmt.Printf("Back to origin. {'%s'}\n", SLICE)
}
Output:
Everything is fine: {'foo'}
Bar? No! {'bar'}
Bar is here: {'bar'}
Back to origin. {'foo'}
Like the Go append built-in function, your appendToSlice function needs to return the result of the append.
func appendToSlice(parts ...byte) []byte {
SLICE = append(SLICE, parts...)
return SLICE
}
and
newSlice = appendToSlice('b', 'a', 'r')
The Go Programming Language Specification
Appending to and copying slices
The built-in functions append and copy assist in common slice
operations. For both functions, the result is independent of whether
the memory referenced by the arguments overlaps.
The variadic function append appends zero or more values x to s of
type S, which must be a slice type, and returns the resulting slice,
also of type S.
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large underlying array that fits
both the existing slice elements and the additional values. Otherwise,
append re-uses the underlying array.
Example:
var b []byte
b = append(b, "bar"...) // append string contents; b == []byte{'b', 'a', 'r' }

Resources