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,以此形成一种链式的关系。所以虽然形式上是值传递,操作上却具备引用的特征。

posted @   Lucky小黄人^_^  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源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题型一
点击右上角即可分享
微信分享提示