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]
Related
Here is the simplified code of a cache. Suppose Container placed in a package, so it don't know about Member.
While I wanna store instances of Member in Container, So I store an empty instance of Member in Container as outerType.
In the Container->GetMysql, I fill a new variable by test values (but, in real world, It fill by data of database, dynamically).
then in the function Put, I store data in items as Cache for next uses. In the Get I get the data stored in the items.
Before this every thing is fine. My problem is where i want to convert result of Get to type of Member m = res.(Member) . How Can I convert it to an instance of Member
I found many question about this subject, but none of them solved my problem
For more detail: I want the Get return data with its pointer of where it stored in items. So if I get some variable of same member, an change in one are shown in others
package main
import (
"fmt"
"reflect"
)
type Member struct {
Id int
Name string
Credit int
Age int
}
type Container struct {
outerType interface{}
items map[string]*interface{}
}
func (cls *Container)GetMysql(s string, a int64) interface{}{
obj := reflect.New(reflect.TypeOf(cls.outerType))
elem := obj.Elem()
//elem := reflect.ValueOf(o).Elem()
if elem.Kind() == reflect.Struct {
f := elem.FieldByName("Name")
f.SetString(s)
f = elem.FieldByName("Credit")
f.SetInt(a)
}
return obj.Interface()
}
func (cls *Container)Get(value string) *interface{}{
return cls.items[value]
}
func (cls *Container)Put(value string, a int64) {
res := cls.GetMysql(value, a)
cls.items[value] = &res
}
func main() {
c := Container{outerType:Member{}}
c.items = make(map[string]*interface{})
c.Put("Jack", 500)
res := c.Get("Jack")
fmt.Println(*res)
m := &Member{}
m = res.(Member) // Here is the problem. How to convert ?
fmt.Println(m)
}
You should hardly ever use pointer to interface. My advice is to never use it, when you'll need it, you'll know.
Instead if you need a pointer to something (so you can have the same pointer at multiple places, and so modifying the pointed value somewhere, it will have effect on the others), "wrap the pointer" in the interface value.
So first modify the items field so that it stores interface{} values instead of pointers:
items map[string]interface{}
This means no restriction: you can pass and store pointers, that's not a problem.
Next modify Get() to return interface{}:
func (cls *Container) Get(value string) interface{}{
return cls.items[value]
}
And also in Put(), don't take the address of an interface{}:
func (cls *Container) Put(value string, a int64) {
res := cls.GetMysql(value, a)
cls.items[value] = res
}
And you have to type-assert *Member from the values returned by Get().
And now testing it:
c := Container{outerType: Member{}}
c.items = make(map[string]interface{})
c.Put("Jack", 500)
res := c.Get("Jack")
fmt.Println(res)
m := res.(*Member) // Here is the problem. How to convert ?
fmt.Println(m)
Output (try it on the Go Playground):
&{0 Jack 500 0}
&{0 Jack 500 0}
Now if you would modify a field of m:
m.Credit = 11
And then get the value form the cache:
fmt.Println(c.Get("Jack"))
We'll see the modified value, even though we did not call Put() (try it on the Go Playground):
&{0 Jack 11 0}
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.
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
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)
}
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' }