5

初学 golang,小白求解惑!

 2 years ago
source link: https://www.v2ex.com/t/817965
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Go 编程语言

初学 golang,小白求解惑!

  superfatboy · 17 小时 12 分钟前 · 1804 次点击
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 貌似没生效

第 1 条附言  ·  16 小时 29 分钟前

习惯了写 js ,学习 go 有点不适应啊!!
22 条回复    2021-11-26 09:46:55 +08:00

rrfeng

rrfeng   17 小时 10 分钟前 via Android   ❤️ 2

for range 是 copy 一份对象,所以没改原始的。
用索引访问数组元素就可以了。

yin1999

yin1999   17 小时 10 分钟前 via Android   ❤️ 1

因为你获取的是一个拷贝,而不是 slice 中的原始对象

helone

helone   17 小时 3 分钟前   ❤️ 1

```
for k, _ := range list {
list[k].Name = "888"
}
```
这样就行了

superfatboy

superfatboy   17 小时 2 分钟前

@rrfeng
@yin1999
感谢,刚才仔细看了一下教程,果然是一个拷贝,nnd ,大意了

corningsun

corningsun   16 小时 59 分钟前

Javaer 震怒 ~

wunonglin

wunonglin   16 小时 49 分钟前   ❤️ 1

```
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

Rooger   16 小时 43 分钟前   ❤️ 1

原因:for range 的方式其实一种 copy 了 list 。
两种修改方式,一种就是 `list[k].Name = 888`,另一种是将 slice 修改为存储指针 var list = []*Test 。
同样的坑你也应该注意,不能使用 k 和 v 的地址。

ClarkAbe

ClarkAbe   13 小时 54 分钟前 via Android

用指针,和 C 系一样

ClarkAbe

ClarkAbe   13 小时 53 分钟前 via Android

不过 js 那边不是有句名言不建议在循环里面修改元素嘛

corningsun

corningsun   13 小时 11 分钟前

@whyso

Java 和 Go 的 for 循环 默认策略完全相反。
Java 必须要主动拷贝才会新建对象。这种 List 结构还得深拷贝,不然用的还是同一个对象。
Go 和 Java stream() 比较像了。

superfatboy

superfatboy   12 小时 58 分钟前

@ClarkAbe 哈哈,怎么顺手怎么来,不过确实不推荐

yrj

yrj   11 小时 13 分钟前 via iPad

你这么理解,range 的时候,其实是 copy 了一份新的数据给 for 所以你在 for 里修改的是 copy 的新数据。可以按照楼上指针的写法,原理是直接操作了指针

mmuggle

mmuggle   10 小时 22 分钟前

虽然 for range 确实是循环的副本,但是这个是循环的切片,切片结构是指向一个底层数组的指针

所以我觉得原因是 v 是在 for 循环的时候初始化的,循环体中修改的是 v 的 Name ,而不是切片元素的 Name

SimbaPeng

SimbaPeng   8 小时 54 分钟前   ❤️ 8

我真的怀疑楼上的回复者,真的会 Golang ?

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

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

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

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

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

最后总结一句话,golang 只有值传递。

SimbaPeng

SimbaPeng   8 小时 23 分钟前

```
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

ila   2 小时 58 分钟前 via Android

range map 时可以修改 map 的 value ,倒推过来看看

cassyfar

cassyfar   2 小时 42 分钟前

@SimbaPeng 这是正解

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

mmuggle

mmuggle   54 分钟前   ❤️ 1

代码其实相当于下面这种
```
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"},
}
v := Test{}
for _, v = range list {
v.Name = "888"
}
fmt.Println(list)
}
```

superfatboy

superfatboy   38 分钟前

@mmuggle 你这种写法就容易理解了

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK