Go 语言为什么建议 append 追加新元素使用原切片变量接收返回值?

简介: Go 语言为什么建议 append 追加新元素使用原切片变量接收返回值?

介绍

在 Go 语言中,切片类型比较常用,将新元素追加到切片也比较常见,因此 Go 语言提供一个内置函数 append,该函数可以非常方便实现此功能。

虽然 Go 语言内置函数 append 使用非常方便,但是使用不当会不小心掉入一些“坑”。

本文我们介绍一下 Go 语言为什么建议 append 追加新元素使用原切片变量接收返回值?

append 的“坑”

我们先看一段示例代码:

func main() {
 a := make([]int, 0, 5)
 a = append(a, 1)
 b := append(a, 2)
 c := append(a, 3)
 fmt.Printf("v=%v || p=%p\n", a, &a) // v=[1] || p=0xc00000c060
 fmt.Printf("v=%v || p=%p\n", b, &b) // v=[1 3] || p=0xc00000c080
 fmt.Printf("v=%v || p=%p\n", c, &c) // v=[1 3] || p=0xc00000c0a0
}

阅读上面这段代码,我们定义一个长度为 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]。为什么呢?我们接着往下看 Part 03 的内容。

03

append 的原理

Go 语言内置函数 append 第一个入参是切片类型的变量,而切片本身是一个 struct 结构,参数传递时会发生值拷贝。

Go 语言 slice 源码如下:

type slice struct {
 array unsafe.Pointer
 len   int
 cap   int
}

因为 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 示例代码为什么实际输出结果和预想的输出结果不同了吧。

04

总结

本文我们介绍 Go 语言中使用内置函数 append 追加新元素的一个“坑”,建议读者朋友们使用原切片变量接收返回值。

推荐阅读:

参考资料:

  1. https://go.dev/tour/moretypes/15
  2. https://pkg.go.dev/builtin#append
  3. https://go.dev/blog/slices-intro
  4. https://go.dev/doc/effective_go#slices
  5. https://go.dev/ref/spec#Slice_types
  6. https://go.dev/ref/spec#Length_and_capacity
  7. https://go.dev/ref/spec#Making_slices_maps_and_channels
  8. https://go.dev/ref/spec#Appending_and_copying_slices
目录
相关文章
|
13天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
25 7
|
12天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
13天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
88 71
|
12天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
97 67
|
13天前
|
存储 Go
go语言中映射
go语言中映射
30 11
|
4天前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
|
15天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
86 62
|
16天前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
15天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
28 12