这篇文章主要介绍了Go for range中容易踩的坑有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go for range中容易踩的坑有哪些文章都会有所收获,下面
这篇文章主要介绍了Go for range中容易踩的坑有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go for range中容易踩的坑有哪些文章都会有所收获,下面我们一起来看看吧。
先来到开胃菜,热热身~
type student struct { name string age int}func main() { m := make(map[string]student) stus := []student{ {name: "张三", age: 18}, {name: "李四", age: 23}, {name: "王五", age: 26}, } for _, stu := range stus { m[stu.name] = stu } for k, v := range m { fmt.Println(k, "=>", v.name) }}
不出意料,输出结果为:
李四 => 李四
王五 => 王五
张三 => 张三
这题比较简单,就是简单的传值操作,大家应该都能答上来。下面加大难度,改为传址操作
将案例一改为传址操作
type student struct { name string age int}func main() { m := make(map[string]*student) stus := []student{ {name: "张三", age: 18}, {name: "李四", age: 23}, {name: "王五", age: 26}, } for _, stu := range stus { m[stu.name] = &stu } for k, v := range m { fmt.Println(k, "=>", v.name) }}
好好想想应该输出什么结果呢?还是跟案例一是一样的结果吗?难道会有坑?
不出意料,还是出了意外,输出结果为:
张三 => 王五
李四 => 王五
王五 => 王五
为什么呢?
首先,关键点在于Go的for循环,对循环变量stu
每次是循环并不是迭代(简单的说,就是对循环变量stu
只会做一次声明和内存地址的分配,后面循环就是不断更新值);
所以,取址操作 &stu
,其实都是取的同一个变量的地址,只是值被循环更新为最后一个元素的值;
最终,输出的v.name
,都是最后一个元素的name为王五
。
解决方案:
在for循环中,做同名变量覆盖stu:=stu
(即重新声明一个局部变量,做值拷贝,避免相互影响)
type student struct { name string age int}func main() { m := make(map[string]*student) stus := []student{ {name: "张三", age: 18}, {name: "李四", age: 23}, {name: "王五", age: 26}, } for _, stu := range stus { stu := stu //同名变量覆盖 m[stu.name] = &stu } for k, v := range m { fmt.Println(k, "=>", v.name) }}
输出结果:
张三 => 张三
李四 => 李四
王五 => 王五
在for循环里,做闭包操作,也是很容易掉坑的。看看下面输出什么?
var prints []func()for _, v := range []int{1, 2, 3} { prints = append(prints, func() { fmt.Println(v) })}for _, print := range prints { print()}
一眼看过去,感觉是输出1 2 3,但实际会输出 3 3 3
为什么呢?
首先,在分析了案例二后,我们知道了Go的for循环对循环变量v
,其实每次是循环并不是迭代;
然后,闭包=函数+引用环境
,在同一个引用环境下,循环变量v的值会被不断的覆盖;
所以最终,在打印时,输出的v,都是最后一个值3。
解决方案:
和案例二解决方案一样,是在for循环中,做同名变量覆盖v:=v
var prints []func()for _, v := range []int{1, 2, 3} { v := v //同名变量覆盖 prints = append(prints, func() { fmt.Println(v) })}for _, print := range prints { print()}
输出结果:
1
2
3
在for循环里,起goroutine协程,也是很迷惑很容易掉坑的。看看下面输出什么?
var wg sync.WaitGroupstrs := []string{"1", "2", "3", "4", "5"}for _, str := range strs { wg.Add(1) go func() { defer wg.Done() fmt.Println(str) }()}wg.Wait()
一眼看过去,感觉是会无序输出1 2 3 4 5,但实际会输出 5 5 5 5 5
为什么呢?
首先,要记得Go的for循环对循环变量str
,其实每次是循环并不是迭代;
然后,main协程会和新起的协程做相互博弈,看谁执行更快,按这个案例执行情况来看,main协程执行速度明显比新起的协程会更快,所以str被更新为最后一个元素值5(备注:并非绝对);
最终,在新起的协程中,使用str时值都为5,作为结果去输出;
拓展:如果在新起协程前,sleep个5s,输出结果又会截然不同,感兴趣的同学可以自行实验下,然后逐步深入地了解下GMP调度机制。
解决方案:
和前面两个案例解决方案一样,是在for循环中,做同名变量覆盖str:=str
var wg sync.WaitGroupstrs := []string{"1", "2", "3", "4", "5"}for _, str := range strs { str := str //同名变量覆盖 wg.Add(1) go func() { defer wg.Done() fmt.Println(str) }()}wg.Wait()
输出结果:
5
4
2
1
3
注意是1~5无序输出
关于“Go for range中容易踩的坑有哪些”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Go for range中容易踩的坑有哪些”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。
--结束END--
本文标题: Go for range中容易踩的坑有哪些
本文链接: https://www.lsjlt.com/news/352235.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
一口价域名售卖能注册吗?域名是网站的标识,简短且易于记忆,为在线用户提供了访问我们网站的简单路径。一口价是在域名交易中一种常见的模式,而这种通常是针对已经被注册的域名转售给其他人的一种方式。
一口价域名买卖的过程通常包括以下几个步骤:
1.寻找:买家需要在域名售卖平台上找到心仪的一口价域名。平台通常会为每个可售的域名提供详细的描述,包括价格、年龄、流
443px" 443px) https://www.west.cn/docs/wp-content/uploads/2024/04/SEO图片294.jpg https://www.west.cn/docs/wp-content/uploads/2024/04/SEO图片294-768x413.jpg 域名售卖 域名一口价售卖 游戏音频 赋值/切片 框架优势 评估指南 项目规模
0