Go 语言为什么建议 append 追加新元素使用原切片变量接收返回值?
看一段代码
func main() {
a := make([]int, 0, 5)
fmt.Printf("%v:%p, len(a) = %d\n", a, &a, len(a))
a = append(a, 1)
fmt.Printf("%v:%p, len(a) = %d\n", a, &a, len(a))
b := append(a, 2)
fmt.Printf("%v:%p, len(b) = %d\n", b, &b, len(b))
c := append(a, 3)
fmt.Printf("%v:%p, len(b) = %d\n", b, &b, len(b))
fmt.Printf("%v:%p, len(c) = %d\n", c, &c, len(c))
}
输出结果:
[]:0xc000096060, len(a) = 0
[1]:0xc000096060, len(a) = 1
[1 2]:0xc0000960a8, len(b) = 2
[1 3]:0xc0000960a8, len(b) = 2
[1 3]:0xc0000960d8, len(c) = 2
阅读上面这段代码,我们定义一个长度为 0,容量为 5 的 int 类型的切片 a。
首先,我们使用 Go 语言内置函数 append 追加一个元素 1 到切片 a 中。
然后,我们使用 Go 语言内置函数 append 追加一个元素 2 到切片 a 中。
最后,我们使用 Go 语言内置函数 append 追加一个元素 3 到切片 a 中。
但是,我们在输出结果中发现,b 的输出结果不是 [1 2],c 的输出结果不是 [1 2 3],b 和 c 的实际输出结果相同,都是 [1 3]。为什么呢?
因为 Go 语言内置函数 append 参数是值传递,所以 append 函数在追加新元素到切片时,append 会生成一个新切片,并且将原切片的值拷贝到新切片。
在 Part 02 示例代码中,我们三次使用 append 参数追加新元素到切片 a 的操作,接收返回值的变量都不同。
第二次操作时,因为 append 生成一个新切片,将原切片 a 的值拷贝到新切片,并且将新元素在原切片a[len(a)] 长度的位置开始追加,使用变量 b 接收 append 返回值 [1 2],所以变量 b 的值是 [1 2]。
第三次操作时,同样 append 生成一个新切片,将原切片 a 的值拷贝到新切片,并且将新元素在原切片a[len(a)] 长度的位置开始追加,使用变量 c 接收 append 返回值 [1 3],所以变量 c 的值是 [1 3]。
但是,因为三个切片的底层数组相同,Go 内置函数 append 会在原切片长度的位置开始追加新元素,所以第三次操作时,把第二次操作时得到的变量 b 的最后一个元素覆盖了。
阅读到这里,相信聪明的读者朋友们已经明白 Part 02 示例代码为什么实际输出结果和预想的输出结果不同了吧。