范围引用而不是值

我看到 range 返回键和值的“拷贝”。有没有一种方法可以让这个范围返回项目的地址?例子

package main


import "fmt"


type MyType struct {
field string
}


func main() {
var array [10]MyType


for _, e := range array {
e.field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}
}

Http://play.golang.org/p/afogg9ngpx

这里没有修改“ field”,因为 range 发送字段的副本。 我必须使用索引还是有其他方法来修改值?

66067 次浏览

简短直接的回答: 不,使用数组索引而不是值

因此,上面的代码变成:

package main


import "fmt"


type MyType struct {
field string
}


func main() {
var array [10]MyType


for idx, _ := range array {
array[idx].field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}
}

在评论中已经说过了,但是对于那些马上寻找答案的人来说,这里介绍了如何通过使用一片指针和对原始代码做最少的修改来实现预期的结果。

package main


import "fmt"


type MyType struct {
field string
}


func main() {
// Slice of pointers instead of slice of type
var array [10]*MyType


// Initialize array manually
for idx := range array {
array[idx] = &MyType{}
}


for _, e := range array {
e.field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}


}

这是 游乐场

package main


import "fmt"


type MyType struct {
field string
}


func main() {
var array [10]MyType


for index := range array {
array[index].field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}
}

结合 @ Dave C@ Sam Toliman的评论

package main


import "fmt"


type MyType struct {
field string
}


func main() {
var array [10]MyType


for idx := range array {
e := &array[idx]
e.field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}
}

Https://play.golang.org/p/knkfzib1nce

Go 的 range只支持按值分配,没有 &range,因为它解决的问题太琐碎了。

预期效果可以实现如下:

for i := range array {
e := &array[i]
e.field = "foo"
}
type arrType []string
type refArrType []*string


func ref(arr arrType) refArrType {
refs := make(refArrType, len(arr))
for i := 0; i < len(arr); i++ {
refs[i] = &arr[i]
}
return refs
}


func main() {
arr := arrType{"hello", "world"}


for _, item := range ref(arr) {
*item = "some other string"
fmt.Println(item, arr)
}
}

我不鼓励你用这个。但是如果您真的希望通过引用来迭代项,那么您可以创建一个新的引用片段(对于空间复杂性来说不是最好的)并在其上循环。

但是无论你在哪里给 item 赋值,你都必须解除对指针的引用并使用它(这就是我认为它不好的地方)。 所以,是的,我不会使用这个解决方案。

而且它只对字符串数组有效。你必须为其他类型创建新的 ref函数: (

package main


import "fmt"


type MyType struct {
field string
}


func main() {
var array [10]MyType


for index, _ := range array {
array[index].field = "foo"
}


for _, e := range array {
fmt.Println(e.field)
fmt.Println("--")
}
}

如果您想访问 翻译的引用值(如在 for i, v := range arr中)以更新数组中对象的值,有三个有希望的(相对简单的)解决方案:

  1. 像第一个答案建议的那样,从数组中更新 [俄语]而不是 翻译(例如,arr[i] = "Hello")

  2. 只有当你的数组包含一组你需要更新的结构,但是在数组的赋值 中没有替换的时候,在你的数组中将 翻译设置为 [俄语],然后通过 翻译更新属性(例如,v := arr[i]; v.thingSheSays = "Hello";)

  3. 或者,我的最爱ーー定义一个包含对象地址的数组。然后使用 for 循环中的指针访问对象。这样做:

输入:

a, b, c := "A", "B", "C"
arr := []*string{&a, &b, &c}
fmt.Println("- Arr Value Updates:")
for i, v := range arr {
*v = "Hello"
fmt.Println("v's value:      " + *v)
fmt.Println("arr[i]'s value: " + *arr[i])
}

产出:

- Arr Value Updates:
v's value:      Hello
arr[i]'s value: Hello
v's value:      Hello
arr[i]'s value: Hello
v's value:      Hello
arr[i]'s value: Hello

希望这个能帮到别人,因为它最初让我作为一个新手难以接受 Golang for-loop。请随意分享您自己的方法来避免这个问题!