Go에서는 기본적으로 모든 함수 인수가 값에 의해 전달됩니다: 변수의 값이 복사됩니다. 그러나 일부 유형(예: 슬라이스, 맵, 채널)은 내부 구조(포인터)에 대한 "포장"입니다. 슬라이스를 값에 의해 전달하면 데이터가 아닌 슬라이스 설명자만 복사됩니다; 두 변수는 동일한 배열을 참조합니다. 구조체의 경우 전체 구조체가 복사됩니다.
복사를 피하고 원본 구조체로 작업하려면 포인터에 의한 전달(*Struct)을 사용합니다.
type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // 복사본만 변경됨 } func updateUserPtr(u *User) { u.Age = 30 // 원본 변경됨 } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }
함수에 전달된 슬라이스의 변경 사항이 항상 함수 외부에서 보이나요?
아니요!
slice[i] = ...)되면 외부에서 보이게 됩니다.slice = append(slice, ...))되면 결과가 함수에서 반환되지 않으면 새로운 요소는 로컬 복사본에 저장되며, 여러분은 그것을 잃게 됩니다.func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100이 추가되지 않음 }
이야기
프로젝트에서 큰 구조체(200+ 바이트)를 값에 의해 채널을 통해 고루틴 간에 전달하였고, 이것이 복사에 엄청난 오버헤드를 발생시키고 성능 손실을 초래했습니다. 포인터에 의한 전달로 전환한 후 latency가 한 단계 줄어들었습니다.
이야기
감사 로그 서비스에서 개발자가 명시적인 복사 없이 맵(map)을 함수 간에 전달하였습니다. 한 함수에서 변경된 사항이 프로그램의 다른 부분에서 예기치 않게 데이터를 변경하여 로그에 혼란을 일으켰습니다.
이야기
함수 내에서 슬라이스를 동적으로 증가시키는 과정에서 새 슬라이스를 반환하는 것을 잊었습니다. 결과적으로 호출하는 코드에 변경 사항이 반영되지 않아 일부 트랜잭션이 손실되었습니다. 함수에서 새 슬라이스를 반환하기로 결정했습니다.