Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go - append to slice on struct does not persist

I have a hard time understanding what seems to be a pretty basic operation. I want to create two structs, where one of the structs will hold a slice of another struct. Here' a simple example to illustration the problem:

// Post structure
type Post struct {
    User string `bson:"user"`
    Body string `bson:"body"`
}

// User structure
type User struct {
    Name  string `bson:"name"`
    Posts []Post `bson:"posts"`
}

func (u *User) appendPost(post Post) []Post {
    u.Posts = append(u.Posts, post)
    return u.Posts
}

func main() {
    p1 := Post{User: "Jane", Body: "First Jane's post"}
    u := User{Name: "Jane"}

    users := []User{}
    users = append(users, u)

    for _, user := range users {
        user.appendPost(p1)
    }

    // [{Jane []}]
    fmt.Println(users)
}

This code produces no errors and also no effect. If I initialize and append a user with the predefined post like this: u := User{Name: "Jane", Posts: []Post{p1}} - everything works as expected. But I can't figure out why the same operation doesn't work in a two steps: 1. Create user with only Name and later append posts in a for loop. Thanks in advance.

This code on the playground

like image 668
curious_gudleif Avatar asked Aug 02 '16 19:08

curious_gudleif


1 Answers

for loop used with range creates a copy of the slice variable. So user above in your for loop is a copy of the User object in your users slice. It's this copy that's getting updated and the reason why you don't see any effect on the desired value. Try rhis:

for i, _ := range users {
    // users[i] now refers to the actual User object in the slice
    users[i].appendPost(p1)
}

Demo: https://play.golang.org/p/CLkNQ2oh0O

Check out For Statement in the language specs here.

like image 137
abhink Avatar answered Nov 11 '22 06:11

abhink