在Go中,大多数内置数据类型(例如int、float、struct)都是可变的,如果它们被声明为变量,因为通过值传递时会复制整个结构或值。但有一个细节:切片(slice)、映射(map)、通道(channel)是引用类型,具有其内部数据存储逻辑。
基本类型:
x := 10 y := x // 这里创建了值的副本,x 和 y 之间没有关系 y = 20 // x 将仍然是 10
切片:
a := []int{1, 2, 3} b := a // 引用同一基本“底层”数组 b[0] = 100 // 现在 a[0] 也变为 100
这很重要,如果函数或方法接收结构或切片,因为更改可能会影响原始数据,也可能不会,具体取决于数据类型和传递方式(按值或按引用)。
问题: "如果将一个切片赋值给另一个,您是得到深拷贝还是两者指向相同的数据?"
答案: 在简单的切片赋值情况下(b := a)—— 两者都指向同一个基本数组,但具有独立的长度和容量。通过一个切片对数组数据的修改会在另一个切片中可见。
示例:
a := []int{1, 2, 3} b := a b[0] = 42 fmt.Println(a) // [42 2 3]
为了进行深拷贝,可以使用copy:
c := make([]int, len(a)) copy(c, a) c[0] = 99 fmt.Println(a) // [42 2 3], c 是独立副本
故事
数据过滤服务开始返回意外结果:一位开发者在不同的函数间传递切片而不进行复制,修改它们的状态。由于在不同地方对切片的修改,应用程序的逻辑被破坏,出现了奇怪的错误,数据问题很长时间无法解决。
故事
结构传递后数据丢失:在一个项目中,序列化数据时意外删除了结构中的必需字段,因为对象通过引用传递,而没有创建其副本。结果导致在生产环境中丢失了关键的信息。
故事
并行操作map的错误:开发者复制了map的引用,认为更改不会影响原始对象,这在其他语言中是常见的。结果在不同的goroutine中发生了数据竞争,导致应用程序崩溃。