Go语言for range使用注意事项 | 青训营笔记

368 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第3篇笔记。

Go语言for range使用注意事项

最近在写青训营抖音项目后端的时候,使用for range时遇到了一个问题。大概流程就是我从数据库查询到了videoList,再通过for range进行遍历,对其中的每个video的IsFavorite、FavoriteCount进行赋值操作,再把videoList返回。具体如下代码所示:

type Video struct {
	Id            int64     `json:"id,omitempty" gorm:"primaryKey"`
	UserId        int64     `json:"user_id"`
	PlayUrl       string    `json:"play_url" json:"play_url,omitempty"`
	CoverUrl      string    `json:"cover_url,omitempty"`
	FavoriteCount int64     `json:"favorite_count,omitempty" gorm:"-"`
	Title         string    `json:"title,omitempty"`
	IsFavorite    bool      `json:"is_favorite" gorm:"-"`
}

func (vs *VideoService) VideoList(userId int64, endTime time.Time) ([]model.Video, error) {
	videoList, err := repository.GroupApp.VideoRepository.ListAll(endTime)
	for _, v := range videoList {
		v.IsFavorite = utils.IsFavorite(userId, v.Id)
		v.FavoriteCount = utils.GetFavoriteCount(v.Id)
	}
	return videoList, nil
}

但运行项目,客户端的IsFavorit、FavoriteCount确实都是默认值,分别为false和0。通过抓包发现,返回的响应数据中的IsFavorit、FavoriteCount的值为false和0。所以定位到了问题所在,for range中进行的赋值操作并为对原videoList造成影响。所以猜测for range时的value是拷贝的副本值。

下面编写demo验证一下:

package main

import "fmt"

type User struct {
	Score int
}

func main() {
	u1 := User{Score: 10}
	u2 := User{Score: 20}

	users := []User{u1, u2}
	fmt.Println(users)
	for _, u := range users {
		u.Score += 10
	}
	fmt.Println(users)
}

输出结果为:

[{10} {20}]
[{10} {20}]

发现users的中每个user的score值并未发生改变。因为这里的u已经不是原来users中的了,而是拷贝出来的副本。当然,map中的结果也是一样

m := make(map[int]User)
for _, u := range m {
    u.Score += 100
}

为了避免这种问题,我们可以采用指针数组

u1 := &User{Score: 10}
u2 := &User{Score: 20}
users := []*User{u1, u2}
fmt.Println(users[0].Score, users[1].Score)
for _, u := range users {
    u.Score += 10
}
fmt.Println(users[0].Score, users[1].Score)

输出结果为:

10 20
20 30

users中的每个user的score值发生了变化。此外还可以使用下标访问,即遍历赋值的时候使用下标来赋值。代码如下:

u1 := User{Score: 10}
u2 := User{Score: 20}
users := []User{u1, u2}
fmt.Println(users)
for i, _ := range users {
    users[i].Score += 10
}
fmt.Println(users)

结果为:

[{10} {20}]
[{20} {30}]
```### [使用Viper管理配置文件,进行MySQL、Redis的初始化 | 青训营笔记](https://juejin.cn/post/7103083841312522271)