初学 golang,小白求解惑!

2021-11-25 17:13:01 +08:00
 superfatboy
package main

import "fmt"

type Test struct {
	Id   int64
	Name string
}

func main() {
	var list = []Test{
		Test{Id: 1, Name: "1XX"},
		Test{Id: 2, Name: "2XX"},
		Test{Id: 3, Name: "3XX"},
	}
	for _, v := range list {
		v.Name = "888"
	}
        fmt.Println(list)
}

为啥修改 Name 貌似没生效

4125 次点击
所在节点    Go 编程语言
27 条回复
rrfeng
2021-11-25 17:15:09 +08:00
for range 是 copy 一份对象,所以没改原始的。
用索引访问数组元素就可以了。
yin1999
2021-11-25 17:15:19 +08:00
因为你获取的是一个拷贝,而不是 slice 中的原始对象
helone
2021-11-25 17:22:20 +08:00
```
for k, _ := range list {
list[k].Name = "888"
}
```
这样就行了
superfatboy
2021-11-25 17:23:11 +08:00
@rrfeng
@yin1999
感谢,刚才仔细看了一下教程,果然是一个拷贝,nnd ,大意了
superfatboy
2021-11-25 17:23:47 +08:00
@helone 感谢
corningsun
2021-11-25 17:26:51 +08:00
Javaer 震怒 ~
wunonglin
2021-11-25 17:36:25 +08:00
```
func main() {
var list = []*Test{
{Id: 1, Name: "1XX"},
{Id: 2, Name: "2XX"},
{Id: 3, Name: "3XX"},
}
for _, v := range list {
v.Name = "888"
}

for _, v := range list {
fmt.Printf("%+v\n", v)
}
}
```

用指针就好了
Rooger
2021-11-25 17:42:41 +08:00
原因:for range 的方式其实一种 copy 了 list 。
两种修改方式,一种就是 `list[k].Name = 888`,另一种是将 slice 修改为存储指针 var list = []*Test 。
同样的坑你也应该注意,不能使用 k 和 v 的地址。
whyso
2021-11-25 17:47:36 +08:00
@corningsun 为啥
superfatboy
2021-11-25 17:53:51 +08:00
@wunonglin 这个方法不错
ClarkAbe
2021-11-25 20:31:35 +08:00
用指针,和 C 系一样
ClarkAbe
2021-11-25 20:32:23 +08:00
不过 js 那边不是有句名言不建议在循环里面修改元素嘛
corningsun
2021-11-25 21:14:50 +08:00
@whyso

Java 和 Go 的 for 循环 默认策略完全相反。
Java 必须要主动拷贝才会新建对象。这种 List 结构还得深拷贝,不然用的还是同一个对象。
Go 和 Java stream() 比较像了。
superfatboy
2021-11-25 21:27:33 +08:00
@ClarkAbe 哈哈,怎么顺手怎么来,不过确实不推荐
yrj
2021-11-25 23:12:41 +08:00
你这么理解,range 的时候,其实是 copy 了一份新的数据给 for 所以你在 for 里修改的是 copy 的新数据。可以按照楼上指针的写法,原理是直接操作了指针
mmuggle
2021-11-26 00:03:21 +08:00
虽然 for range 确实是循环的副本,但是这个是循环的切片,切片结构是指向一个底层数组的指针

所以我觉得原因是 v 是在 for 循环的时候初始化的,循环体中修改的是 v 的 Name ,而不是切片元素的 Name
SimbaPeng
2021-11-26 01:31:48 +08:00
我真的怀疑楼上的回复者,真的会 Golang ?

什么 range copy 了 list ,range 策略,深拷贝浅拷贝?能不能不要在这里误人子弟?

这里的关键就是 v := range list 是个赋值操作,将 list 元素赋值给 v ,golang 里只有值传递,所以 list 元素在复制给 v 的时候必然产生了拷贝。而结构体是值类型,没有引用的对象,所以是拷贝整个结构体,直接修改 v 的属性就是修改拷贝的结构体,所以原结构体属性并不会改变。

同理,但凡你将一个结构体赋值给一个新变量,然后修改新变量的字段,都不会对原结构体有任何影响。

这跟你用没用 range 一点关系都没有。

如果你的 list 里存的是结构体的指针,range 的时候一样会产生拷贝,不过拷贝的是指针结构,底层引用的结构体不会产生拷贝,所以你解指针修改字段的时候,修改的是同一个结构体。但如果将一个新的指针赋值给 v ,原 list 的元素依然不会被改变。

最后总结一句话,golang 只有值传递。
SimbaPeng
2021-11-26 02:02:01 +08:00
```
func main() {
a := []int{1, 2, 3}
for i, v := range a {
if i == 0 {
a[2] = 4
}
fmt.Println(v)
}
}
```

那些说是因为 range 的是 list 的副本的,自己运行一下这段代码。copy 的是切片,又不是底层数组。
ila
2021-11-26 07:27:25 +08:00
range map 时可以修改 map 的 value ,倒推过来看看
cassyfar
2021-11-26 07:43:53 +08:00
@SimbaPeng 这是正解

所以如果你要用 for loop 改值,换成指针就好了。实际操作中,也基本用的指针数组存放 struct 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/817965

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX