For example:
package main
import "fmt"
type Test struct {
elems []string
}
func main() {
initial := Test{
elems: make([]string, 0),
}
initial.elems = append(initial.elems, "apple")
fmt.Println(initial.elems) // #1 [apple]
s := make([]Test, 0)
s = append(s, initial)
initial.elems = append(initial.elems, "bannana")
fmt.Println(initial.elems) // #2 [apple bannana]
fmt.Println(s[0].elems) // #3 [apple]
second := s[0]
second.elems = append(second.elems, "carrot")
fmt.Println(second.elems) // #4 [apple bannana]
}
I am looking for help understanding print statements #3 and #4. In #3 I expect [apple bannana]
and in #4 I am expecing [apple bannana carrot]
.
It is my understanding that the elems
field which is a slice is automatically passed by reference and therefore each append that I do in the above block of code should modify the underlying array. But, apparently that is not the case.
So, my question is: What happens to initial
when it gets inserted into a slice that makes this not work? Also, how would one write this code to get the expected result at print statement #4?
In Golang it is mentioned:
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.
The way you are appending to the slice s
is creating a new slice by adding the copy of Test struct to the s
slice. Hence you are not setting a pointer to the original Test
struct. So that if data changes inside the struct it will be reflected in the slice too. This is the problem you are facing.
initial.elems = append(initial.elems, "apple")
fmt.Println(initial.elems) // #1 [apple]
s := make([]Test, 0) // this should be pointer to the struct to have teh changes in future to original struct.
s = append(s, initial) // appending to the s slice
Create a pointer to the Test
struct when making slice s
, which will reflect the change whenever you change the elems inside original struct. For example:
package main
import "fmt"
type Test struct {
elems []string
}
func main() {
initial := Test{
elems: make([]string, 0),
}
initial.elems = append(initial.elems, "apple")
fmt.Println(initial.elems) // #1 [apple]
s := make([]*Test, 0) // create a pointer to Test struct.
s = append(s, &initial)
initial.elems = append(initial.elems, "bannana")
fmt.Println(initial.elems) // #2 [apple bannana]
fmt.Printf("%+v
",*s[0]) // #3 [apple banana]
second := s[0]
second.elems = append(second.elems, "carrot")
fmt.Println(second.elems) // #4 [apple bannana carrot]
}
Output:-
[apple]
[apple bannana]
{elems:[apple bannana]}
[apple bannana carrot]
Working Code on Go Playground
Its caused by fact, that the initial
variable isn't the same as s[0]
- they are two independent Test
variables and appending to one doesn't change the second. initial
is being copied by value to different object when passed to append()
Proof:
fmt.Printf("second: %p, initial: %p ", &second.elems[0], &initial.elems[0])
(where second.elems[0] == "apple"
and initial.elems[0] == "apple"
) outputs
second: 0xc00000a120, initial: 0xc00000a0c0
which shows, that this is true