[golang] 반복문 값을 변경할 때 발생하는 문제(range는 어떻게 동작하는가)

hyomin·2020년 1월 19일
0

golang-grammer

목록 보기
3/3
post-thumbnail

개요

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

0개의 댓글