在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+字节)的数据结构通过通道按值传递给goroutines,这导致了巨大的复制开销和性能损失。在切换到按指针传递后,延迟减少了一个数量级。
故事
在审计日志服务中,开发人员在函数之间传递地图(map),而没有显式克隆(copy)。一个函数中的更改意外地更改了程序另一部分的数据,导致日志混乱。
故事
在动态增加切片的函数中,忘记将新的切片返回。结果,修改没有反映在调用代码中,导致部分事务丢失。决定从函数中返回新的切片。