Reputation: 3193
I'm still in the "wrestling with the language" stage of my Go progress, so forgive me for almost definitely missing something very obvious here.
I'm defining two structs, one containing the other. I make an array of the outer structs, pass it to a function, which calls a method on each of the inner structs, modifying their contents. This change is visible within the function, but when adding the outer structs to an array for returning, the outer function can't see the changes. I've tried throwing pointers in everywhere to little avail - somewhat thankfully, because it looked awful.
package main
import "github.com/davecgh/go-spew/spew"
type inner struct {
ints []int
}
func (i *inner) grow() {
i.ints = append(i.ints, 0)
}
type outer struct {
inner inner
}
func growOs(os []outer) (out []outer) {
for _, o := range os {
o.inner.grow()
out = append(out, o)
}
spew.Dump("----------during")
spew.Dump(out)
return
}
func main() {
os := []outer{
outer{},
outer{},
}
spew.Dump("----------pre")
spew.Dump(os)
growOs(os)
spew.Dump("----------post")
spew.Dump(os)
}
Upvotes: 1
Views: 937
Reputation: 31109
If a function expects pointer argument it will obtain a pointer to an object thereby each change executed upon the object will have effect on the object itself. If a function expects a value argument it will obtain a copy of an object so each operation will affect an obtained copy not the object.
func recPointer(s *string) {
fmt.Printf("%p\n", s) // The same object
}
func recValue(s string) {
fmt.Printf("%p\n", &s) // A new object
}
func main() {
s := "asdf"
fmt.Printf("%p\n", &s)
recPointer(&s)
recValue(s)
}
Go's return values may be named. If so, they are treated as variables defined at the top of the function.
Thereby it becomes clear that named return values are defined in function not in a name space above it. So when you return a named value you do it with a new object (again).
func recValue(s []string) (out []string) {
fmt.Printf("%p\n", &s) // A new object
out = append(out, s...)
// Again new object. It is not the `s` from `main`
// and obviously not the `s` var from incoming arguments.
fmt.Printf("%p\n", &out)
return
}
func main() {
s := []string{"asdf"}
fmt.Printf("%p\n", &s)
recValue(s)
}
Be careful! I replaced spew.Dump
with fmt.Printf
for demonstrative debug.
// No named return values
// Pointer argument receiver
func growOs(os *[]outer) *[]outer {
for _, o := range *os {
o.inner.grow()
*os = append(*os, o)
}
fmt.Printf("%p: %+v \n", os, os)
return os
}
func main() {
// `os` is a pointer to the object
os := &[]outer{
outer{},
outer{},
}
fmt.Printf("%p: %+v \n", os, os)
growOs(os)
fmt.Printf("%p: %+v \n", os, os)
}
Upvotes: 1
Reputation: 10254
You should change
func main() {
os := []outer{
outer{},
outer{},
}
spew.Dump("----------pre")
spew.Dump(os)
growOs(os)
spew.Dump("----------post")
spew.Dump(os)
}
to
func main() {
os := []outer{
outer{},
outer{},
}
spew.Dump("----------pre")
spew.Dump(os)
os = growOs(os)
spew.Dump("----------post")
spew.Dump(os)
}
NOTICE: os = growOs(os)
in function
func growOs(os []outer) (out []outer) {
for _, o := range os {
o.inner.grow()
out = append(out, o)
}
spew.Dump("----------during")
spew.Dump(out)
return
}
you just return a new slice out
, you don't modify the input slice os
,
So in your main
function, the os
isn't changed.
Upvotes: 2
Reputation: 99195
The o
in your loop is a copy, so you either have to:
[]*outer
.Ex:
for i := range os {
o := &os[i]
o.inner.grow()
// or just os[i].inner.grow()
}
return os
os = growOs(os)
Upvotes: 3