golang sync.Map/context.Context 在函数传参时默认是值传递
概要
今天改了一段代码,调用函数时把一个 sync.Map 类型的变量作为传参,在函数内部修改 sync.Map 变量的值,发现函数处理完成后,外部的变量没有修改,后来发现 sync.Map 是一个普通的结构体,跟所有的结构体一样,golang 在把结构体类型作为函数传参时,都是值传递,即创建参数的一个副本,然后传递给函数。这也就意味着,如果你在函数内部修改了这个副本的值,那么原始的值是不会被影响的。
验证过程
函数参数类型为 *sync.Map
从运行结果来看,函数内部的修改影响了原始变量。
func main() {
var result sync.Map
result.Store(10, 11)
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true // 返回 false 则表示停止迭代
})
fmt.Println("---------------------------")
GetTaskCountValueMap(&result) // 传的是指针类型
fmt.Println("---------------------------")
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true
})
}
func GetTaskCountValueMap(result *sync.Map) {
for i := 0; i < 3; i++ {
result.Store(i, i+1)
}
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true
})
}
输出结果
key=10 val=11
---------------------------
key=0 val=1
key=1 val=2
key=2 val=3
key=10 val=11
---------------------------
key=10 val=11
key=0 val=1
key=1 val=2
key=2 val=3
函数传参类型为 sync.Map
从运行结果来看,函数内部的修改不会影响原始变量。
func main() {
var result sync.Map
result.Store(10, 11)
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true // 返回 false 则表示停止迭代
})
fmt.Println("---------------------------")
GetTaskCountValueMap(result) // 传的是普通结构体类型
fmt.Println("---------------------------")
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true
})
}
func GetTaskCountValueMap(result sync.Map) {
for i := 0; i < 3; i++ {
result.Store(i, i+1)
}
result.Range(func(key, value any) bool {
fmt.Printf("key=%v val=%v\n", key, value)
return true
})
}
输出结果
key=10 val=11
---------------------------
key=10 val=11
key=0 val=1
key=1 val=2
key=2 val=3
---------------------------
key=10 val=11
golang 函数传参提醒
结构体、数组和基本数据类型都是值传递
在Go语言中,无论是结构体还是基本数据类型,当他们作为函数参数传递时,都是值传递。如果在函数内部修改了副本的值,原始的值是不会被影响。
切片、map 是引用传递
切片看起来是被作为值传递,但实际上切片(slice)的数据结构包含了指向底层数组的指针。这就意味着,当你将切片传递给函数时,虽然它是传的值(也就是切片自身的数据结构被复制),但是复制的切片和原始的切片依然共享同一个底层数组。因此,如果你在函数中修改了复制的切片中的元素,原始切片相对应的元素也会被修改。
跟切片类似,map 传递的也是对底层数据结构的引用
特别提醒:context.Context 在函数传参时也是值传递的
context.Context 是值传递,每次函数会自动复制一个新的变量,所以如果在函数内部修改了ctx 的值,并不会影响外部的 ctx,如果需要外部的context 也被修改,需要把ctx return 返回,或者传参时使用指针类型,*context.Context。
context.Context 其他描述
Go 语言的 Context 在函数参数传递时是以值的形式传递的。然而,Context 应被视为不可变的,你永远不应该更改其内容,只能使用 With** 函数创建新的 Context。
在 Go 中,虽然传递的是 Context 的值,但由于它包含的是对底层数据结构的引用,因此当通过 WithCancel,WithDeadline,WithTimeout 或 WithValue 修改 Context 时,你其实是创建了新的 Context,而这个新的 Context 会持有老的 Context,以此形成一种链式的关系。所以虽然形式上是值传递,操作上却具备引用的特征。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
2021-09-05 docker学习笔记
2019-09-05 dfs题型二(迷宫问题)
2019-09-05 dfs题型一