range를 사용하며 반복문을 실행하는 경우,
언뜻보기에 index와 value를 사용하여 값을 변경할 수 있을 것 같아 보입니다.
그러나,
range는 기존의 배열/슬라이스를 복사하기 때문에 우리의 인식과는 다른 동작을 하게 됩니다.
아래는 다른 개발자의 고민과 그 답변을 스택오버플로우에서 가져온 것입니다.
아래와 같은 타이프를 가진다고 가정합니다.
type Attribute struct {
Key, Val string
}
type Node struct {
Attr []Attribute
}
그리고 각 노드의 속성을 반복하며 변경하고 싶다고 가정했을 때
아래와 같이 처리하고 싶었습니다.
for _, attr := range n.Attr {
if attr.Key == "href" {
attr.Val = "something"
}
}
그러나 attr이 포인터가 아니기 때문에, 동작하지 않을 것이고 아래와 같이 해야만 합니다.
for i, attr := range n.Attr {
if attr.Key == "href" {
n.Attr[i].Val = "something"
}
}
더 간단하고 빠른 방법이 있을까요? range로 부터 pointer들을 직접 얻을 수 있는 방법이 있을까요?
단지 반복처리를 하기 위해 structure를 변경하고 싶지는 않습니다.
더 간단한 방법은 없습니다.
왜냐하면 range는 당신이 반복처리하는 slice로부터 값들을 복사하기 때문입니다.
명세를 보면 range는 아래와 같이 설명합니다.
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E
string s string type index i int see below rune
map m map[K]V key k K m[k] V
channel c chan E, <-chan E element e E
따라서 range는 array/slice의 a[i]를 두 번째 값으로 사용하며, 이는 기존 값을 변경불가능한 상태로 유지하며 값들을 복사한다는 것을 효과적으로 설명합니다.
이러한 동작은 아래 코드로 증명됩니다.
x := make([]int, 3)
x[0], x[1], x[2] = 1, 2, 3
for i, val := range x {
println(&x[i], "vs.", &val)
}
이 코드는 range의 각 값과 slice의 실제 값이 다른 메모리 위치를 가진다는 것을 알 수 있습니다.
0xf84000f010 vs. 0x7f095ed0bf68
0xf84000f014 vs. 0x7f095ed0bf68
0xf84000f018 vs. 0x7f095ed0bf68
따라서 우리가 할 수 있는 유일한 방법은 pointer나 index를 시용하는 것입니다.
https://stackoverflow.com/questions/15945030/change-values-while-iterating