0

I'm new to go so forgive me if this is a trivial question. I want to iterate over a slice of posts and increment the value of Views of each post:

    func incrementViews(posts []model.Post) []model.Post {
        for _, v := range posts { 
            v.Views++
            fmt.Println(v.Views) //Views incremented by 1
        }
        return posts
    }

    incrementViews(posts) //Views not changed

The printed values are changed but when I call incrementViews(posts) the returned values are unchanged.

I tried to solve this by using * of & but could not manage to do so perhaps because I come from Python background and have lose grasp of moving around variables by pointers and values.

Karlom
  • 13,323
  • 27
  • 72
  • 116

2 Answers2

1

The code in the question is updating the local variable v. Either change the slice to *model.Post or update the value in the slice using the index operator. The former requires changes to the caller.

func incrementViews(posts []*model.Post) []*model.Post {
    for _, v := range posts { 
        v.Views++
    }
    return posts
}

func incrementViews(posts []model.Post) []model.Post {
    for i := range posts { 
        posts[i].Views++
    }
    return posts
}

EDIT:

Both approaches works, see here: https://play.golang.org/p/90BNOFYaKL

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
  • The first method causes error: `cannot use posts (type []model.Post) as type []*model.Post in argument to incrementViews` – Karlom Jul 14 '17 at 03:16
  • @sahaj Then I get 2 errors: `cannot use posts (type []*model.Post) as type []model.Post in return argument` and `cannot use &posts (type *[]model.Post) as type []*model.Post in argument to incrementViews` – Karlom Jul 14 '17 at 03:24
  • @Karlom I believe please be polite with your comment. Just added a play example link. – jeevatkm Jul 14 '17 at 03:49
  • @Karlom it is called from `for each` line on `main` func. Kindly have a look. – jeevatkm Jul 14 '17 at 03:59
  • @jeevatkm your playground examples are not same as my use case. You iterate OVER function. What I'm trying to do is to iterate INSIDE function. – Karlom Jul 14 '17 at 03:59
  • 2
    @Karlom I'm not sure what are you trying to say. Methods `incrementViews1` and `incrementViews2` takes slice as an input and changes the values then return to caller on main func. I'm just iterating the result instead of storing in the variables. But result will be same. You can modify and run it. you will see. – jeevatkm Jul 14 '17 at 04:07
  • @jeevatkm, sorry I made a stupid mistake. Since the slice was loaded from database each time it rest to the previous value! – Karlom Jul 15 '17 at 18:41
  • @Karlom I'm glad you figured out the issue at your end. Good luck. – jeevatkm Jul 15 '17 at 18:59
1

The range expression returns a copy of slice element. Therefore, you should be very careful when you want to modify slice element while iterating.

The range expression on slice or an array returns first parameter as index and second parameter as copy of element at that index. In your example, you are modifying copy returned by range and hence not getting the modification in the original slice element.

What you need to change is, refer slice name with [index], so that you actually refer to original element in the slice and hence can modify the original slice element. Refer to second approach in the working example given by jeevatkm.

The other option is to use slice of addresses, in this case you can refer to value returned by range, as even it is a copy, it is a copy of address location and it still points to the original element. Refer to first approach in the working example given by jeevatkm.

sahaj
  • 822
  • 5
  • 17
  • Good lecture. But, how does this translate to working code? – Karlom Jul 14 '17 at 04:13
  • @shaj you just copied the playground in other answer comments which is irrelevant to my case, I do NOT iterate over `incrementViews` function to get values of structs inside slice changed. – Karlom Jul 14 '17 at 04:36
  • Not able to understand, what you are trying to say? Can you please put your sample code on playground and explain the exact doubt? – sahaj Jul 14 '17 at 05:10
  • 1
    Are you intending this: https://play.golang.org/p/HICoue8Qp6. As shown in this example, you can create a `newPosts` slice to hold the changed elements and then return that. – sahaj Jul 14 '17 at 05:27
  • Yes, that's want to achieve, though without creating a new slice inside the function, if possible. – Karlom Jul 14 '17 at 05:31
  • That is exactly jeevatkm playground example has demonstrated. – sahaj Jul 14 '17 at 06:22