질문 역사:
for-range 구조는 Go에서 컬렉션(슬라이스, 배열, map, 문자열) 반복을 위한 방법으로 등장했습니다. Go 개발자들은 최적화를 도입했습니다: 루프의 각 반복에서 값이 복사되고, 참조를 직접 사용하지 않기 때문에, 이는 루프 변수와 관련하여 예상치 못한 오류를 초래할 수 있습니다.
문제:
많은 사람들이 range 내 변수의 주소를 가져오려고 할 때(예: &v) 컬렉션의 요소 주소를 얻는다고 착각하지만, 실제로는 지역 변수의 주소를 얻고 있습니다.
해결책:
for-range 루프의 각 반복에서 이터레이터 변수(key, value)의 새로운 복사본이 생성됩니다. 간단한 유형의 경우에는 문제가 없지만, 구조체의 경우 요소에 대한 포인터를 유지하면 항상 같은 변수를 가리키게 되어 슬라이스의 서로 다른 요소를 가리키지 않게 됩니다.
코드 예:
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // 모든 ptrs는 동일한 p를 참조하게 됩니다. }
주요 특징:
range 내에서 value 변수에 대한 참조를 저장하면 어떻게 됩니까?
모든 참조는 같은 메모리를 가리키게 되며, value는 임시 변수입니다.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // 모든 ptrs는 동일한 변수를 참조합니다!
range에서 value를 통해 컬렉션의 요소를 참조로 변경할 수 있습니까?
아니요, value를 변경해도 원래 컬렉션의 요소에는 영향을 미치지 않습니다. 변경하려면 인덱스를 통해 참조해야 합니다.
for _, v := range arr { v.Field = 10 // arr는 변경되지 않습니다. } for i := range arr { arr[i].Field = 10 // 올바른 방법입니다. }
map에 대한 for-range가 순차적인 순서를 보장합니까?
아니요, Go에서 map에 대한 반복 순서는 정의되어 있지 않으며, 각 애플리케이션 실행 시 다를 수 있습니다.
개발자가 range를 통해 구조체 요소의 링크 목록을 직렬화하려고 하고, &value를 별도의 슬라이스에 저장합니다. 결과적으로 동일한 주소의 슬라이스가 생성됩니다.
장점:
단점:
인덱스를 통해 반복하고 원하는 배열 요소의 포인터를 저장합니다:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
장점:
단점: