143

I saw that range returns the key and the "copy" of the value. Is there a way for that range to return the address of the item? Example

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

Here "field" is not modified because range sends the copy of field. Do I have to use index or is there any other way to modify the value?

Xavi
  • 20,111
  • 14
  • 72
  • 63
Titouan56
  • 6,932
  • 11
  • 37
  • 61
  • 4
    possible duplicate of [Change values while iterating in golang](http://stackoverflow.com/questions/15945030/change-values-while-iterating-in-golang) – James Henstridge Nov 25 '13 at 08:16
  • 1
    Can you use an array of pointer of `MyType`? – nvcnvn Nov 26 '13 at 01:45
  • Yes, if you use an array of pointers you can modify the results directly in the array without having to use the index, there is an example here http://play.golang.org/p/_Vx7ONLDJs – notzippy Nov 10 '14 at 05:19
  • `range` is working fine & it returns what you've put inside the slice. Here values of type `MyStruct` (which is a value type) reside inside the slice. Instead we could put pointers of type `*MyStruct` inside the slice. If you absolutely need it to work the way it is, you could use indexes instead of values returned by `range`. – Kaveh Shahbazian Feb 25 '17 at 21:46

8 Answers8

193

The short & direct answer: no, use the array index instead of the value

So the above code becomes:

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("--")
    }
}
MushinNoShin
  • 4,695
  • 2
  • 32
  • 46
  • 42
    And of course, if you repeatedly access `array[idx]` you could instead choose to have `e := &array[idx]` at the top of the for loop and then use `e.field1`, `e.field2`, etc which more closely resembles the OP might have wanted (just with two lines instead of one). – Dave C Apr 07 '15 at 18:43
  • 32
    You can drop the `, _` entirely - `for idx := range array` – Sam Toliman Oct 29 '18 at 17:52
  • 1
    Wondering why Go does copy the reference to the second value for the range. Copying the value there is barely useful, or is it? –  Aug 01 '22 at 23:01
11

To combine @Dave C and @Sam Toliman's comments

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

Sam Lee
  • 111
  • 1
  • 3
9

Go's range only supports assignment by value. There is no &range because the problem it solves is too trivial.

The desired effect can be achieved as follows:

for i := range array {
    e := &array[i]
    e.field = "foo"
}
Grant Zvolsky
  • 483
  • 5
  • 6
2
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("--")
    }
}
Nick Vu
  • 14,512
  • 4
  • 21
  • 31
1

If you would like to access v's reference value (as in for i, v := range arr) to update the value of the object in the array, there are three promising (relatively simple) workarounds:

  1. Like the first answer suggested, update arr[i] instead of v from within the array (e.g., arr[i] = "Hello")

  2. ONLY IF your array contains a set of structs you need to update but not replace in the array's assignment, set v to arr[i] within your array and then update properties through v (e.g., v := arr[i]; v.thingSheSays = "Hello";)

  3. Or, my favorite—define an array containing the object addresses. Then access the objects using a pointer from within the for-loop. Do this like so:

Input:

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])
}

Output:

- 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

Hope this was able to help someone, as it initially stumped me as a newbie to golang for-loops. Feel free to share your own methods for avoiding this issue!

Gabe Tucker
  • 127
  • 9
0
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)
    }
}

I won't encourage to use this. But if you really want to iterate over items by references then you can make a new slice of refs (not best for space complexity) and loop over that.

But with that wherever you are assigning new value to item you'll have to dereference the pointer and use it (that is what makes it bad in my opinion). So yes I won't use this solution.

And also it works only for array of strings. You have to make new ref function for other types :(

nTheta
  • 539
  • 3
  • 12
-1

It's been said in the comments already, but for those looking for answers right away, here's how you can achieve expected result by using a slice of pointers and by making the least changes to the original code.

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("--")
    }

}

Here it is in playground

Jairo Lozano
  • 3,883
  • 3
  • 20
  • 29
  • 3
    The fact is, that pointer is useful only for this range use. I would prefer to add `e := &array[idx]` inside every range that have that pointer to a pointer... – Cirelli94 Feb 27 '20 at 14:26
  • That's completely off-topic — you're iterating over a completely different data structure. – jch Jun 13 '21 at 22:26
-2
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("--")
    }
}
jukay
  • 11
  • 3
  • 3
    This does not seem to add anything to the existing accepted answer (posted 6 years ago). – Brits Sep 15 '20 at 03:09