문제의 역사:
Go는 명시적인 값 의미(value semantic)를 가진 언어로 설계되었습니다. 구조체(struct)를 포함하여 대부분의 값이 호출 시 복사되지만, 포인터와 슬라이스(slice)는 제외됩니다. 이는 논리를 단순화하고 안전성을 높였지만 여러 가지 "함정"을 초래했습니다.
문제:
개발자들은 종종 함수에 구조체를 전달할 때 그 변경 사항이 "외부"에서도 보일 것이라고 예상합니다. 그러나 모든 내용이 복사됩니다(내부 필드도 값에 의해!). 슬라이스와 맵은 "내용물"이 아닌 "용기"만 복사되는 다른 동작을 보입니다.
해결책:
변경이 예상되는 경우 큰 구조체는 포인터를 통해 전달하세요. 슬라이스는 내용물이 아닌 설명자(length, capacity, pointer)만 복사됩니다. 원본 슬라이스의 변경(인덱스를 통해)은 외부에서 보입니다. 구조체는 모든 것이 복사됩니다:
type Point struct { X, Y int } func move(p Point) { p.X = 100 } func movePtr(p *Point) { p.X = 100 } func demo() { pt := Point{10, 10} move(pt) fmt.Println(pt.X) // 10 movePtr(&pt) fmt.Println(pt.X) // 100 }
주요 특징:
함수 내부에서 구조체를 변경하면 원본도 변경되나요?
아니요, 구조체가 값으로 전달되면 변경 사항은 로컬입니다.
type User struct {Name string} func f(u User) {u.Name = "Ann"}
함수 내부에서 슬라이스의 요소를 변경하면 원본도 변경되나요?
네. 슬라이스는 공유 배열에 대한 "뷰"입니다. 요소를 변경하면 원본 데이터도 변경됩니다.
func f(s []int) {s[0] = 99}
함수 내부에서 생성된 슬라이스를 반환하면 어떻게 되나요?
슬라이스의 "헤드"는 복사되지만, 기본 배열은 여전히 접근 가능합니다. 외부에서 참조를 저장하지 않으면 데이터는 GC에 의해 수집될 수 있습니다.
User 구조체를 값으로 처리하는 함수가 있었고, 변경 사항이 돌아오지 않아 버그를 찾기 어려웠습니다.
장점:
단점:
큰 구조체는 명시적으로 포인터로 전달되고 슬라이스에 대한 동작이 항상 주석으로 처리되거나 확인됩니다. 혼란이 없어 모든 사람이 값 의미(value-semantic)를 예상합니다.
장점:
단점: