首页 > 其他分享 >Go 语言 for-range 的两个坑,你踩过吗?

Go 语言 for-range 的两个坑,你踩过吗?

时间:2023-07-09 22:35:09浏览次数:44  
标签:语言 fmt Println range func time Go main

坑一:迭代时协程引用索引和值

先看看下面的例子,你知道最终输出的结果是什么吗?

package main

import (
	"fmt"
	"time"
)

func main() {
	var m = []int{1, 3, 5}
	for i, v := range m {
		go func() {
			fmt.Println(i, v)
		}()
	}
	time.Sleep(time.Second)
}

不知道的同学,大家可以在 https://play.golang.org/ 这里尝试运行一下。

正确答案是:

2 5
2 5
2 5

这是为啥?三个 goroutine 都是输出了最后迭代的索引和值。

我觉得,理解清楚以下两点就可以了:

  1. 闭包内引用了外部变量 i 和 v,三个协程都引用了
  2. 协程运行时,循环可能已经结束了

要想解决这个问题,可以改成闭包传参的形式:

func main() {
	var m = []int{1, 3, 5}
	for i, v := range m {
		go func(_i, _v int) {
			fmt.Println(_i, _v)
		}(i, v)
	}
	time.Sleep(time.Second)
}

也可以让每一轮循环都用新的变量:

func main() {
	var m = []int{1, 3, 5}
	for i, v := range m {
		_i := i
		_v := v
		go func() {
			fmt.Println(_i, _v)
		}()
	}
	time.Sleep(time.Second)
}

运行结果就符合预期了:

0 1
1 3
2 5

坑二:迭代时值为原先迭代对象的拷贝

package main

import (
	"fmt"
)

func main() {
	var m = []int{1, 3, 5}
	for i, v := range m {
		if i == 1 {
			v = 2
		}
		fmt.Println(i, v)
	}
	fmt.Println(m)
}

这个输出是啥?

0 1
1 2 // 明明改成 2 了,
2 5
[1 3 5] // 这里还是 3 ?

那是因为,Go 会在 range 循环中自动为遍历的对象创造一个副本,可以理解为一个值拷贝,如果真的想修改原数组,你得这样写:

func main() {
	var m = []int{1, 3, 5}
	for i, v := range m {
		if i == 1 {
			m[i] = 2 // 改成 m[i]
		}
		fmt.Println(i, v)
	}
	fmt.Println(m)
}

程序输出是这样:

0 1
1 3 // 由于是值拷贝,所以改了原来的 m[i] 不影响 v 的值,m[i]=2,v=3.
2 5
[1 2 5] // 可以看到已经改成 2 了

也不算是什么大坑,理解了感觉和 PHP 的 foreach 语法差不多。


文章来源于本人博客,发布于 2019-06-16,原文链接:https://imlht.com/archives/187/

标签:语言,fmt,Println,range,func,time,Go,main
From: https://www.cnblogs.com/lofanmi/p/17539565.html

相关文章

  • Docker CLI docker login和logout 常用命令
    Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化。Docker是内核虚拟化,不使用Hypervisor是不完全虚拟化,依赖内核的特性实现资源隔离。本文主要介绍DockerCLI中d......
  • django values 与values_list的区别
    values values()方法返回包含字典的QuerySet<QuerySet[{'comment_id':1},{'comment_id':2}]>values_listvalues_list()方法返回一个包含元组的QuerySet<QuerySet[(1,),(2,)]>如果您使用values_list()单个字段,则可以使用flat=True返回单个值的QuerySet而不是1个元......
  • 为什么做GO/KEGG富集分析
     在进行差异表达分析的时候,我们会得到很多的差异表达基因,富集分析可以把这些差异基因概述成整体事件。A信号通路与症状有关,而不是A1/A2/A3等基因与症状有关。GO和KEGG就是基于不同的分类,而储存的基因相关功能的数据库。  利用GO数据库,我们就可以得到我们的目标基因在CC,MF和......
  • Django 使用 ImageKit 进行的ImageField 图像处理
     有图像的话,肯定不知保存,需要改变图像的像素,大小等,这就需要第三方的libpipinstall-Udjango-imagekitpipinstall-UPillow settings.py里面,追加imagekit  ,MEDIA_URL,MEDIA_ROOT model.pyfromdjango.dbimportmodelsfromimagekit.modelsimportImageSp......
  • R语言 ggplot函数中 annotate选项增加注释
     001、基础绘图ggplot(data=mtcars,aes(x=mpg,y=disp,color=factor(cyl)))+geom_point()##基础绘图 002、annotete在任意位置增加注释ggplot(data=mtcars,aes(x=mpg,y=disp,##在坐标,25,300处增加QQcolor=factor(cyl)))+geom_point......
  • 关于django-storages
    如果djangofileField,imageField不是默认存在本地服务器,而是远程云服务器上,则使用django-storages可以对应很多云服务器如AmazonS3AzureStorageDropBoxGoogleCloudStorageApacheLibcloudFTP/SFTP 文件保存访问路径django默认MEDIA_ROOTMED......
  • 详解Django请求与响应:深入理解Web Http交互的核心机制
    本文深入探讨了Django中的请求与响应处理,从Django请求和响应的基础知识、生命周期,到HttpRequest和HttpResponse对象的详细介绍。同时,讨论了Django的视图和请求、响应处理,以及安全性和异步处理的考虑。最后,对比了Django与Flask、FastAPI等框架在请求响应处理上的异同......
  • 图形化编程教学视频(以scratch为编程语言)
    新版少儿编程scratch3.0从入门到精通系列教程p1:课前准备p2:电脑里的海洋馆p3:钢琴少年p4:索罗变身p5:航海王路飞p6:蟠桃盛宴p7:消灭蚊子p8:模拟家用电器p9:欢乐体育课p10:我要去火星p11:五官拼拼乐p12:影子分身术......
  • 四种语言刷算法之子集 II
    力扣90. 子集II1、C/***Returnanarrayofarraysofsize*returnSize.*Thesizesofthearraysarereturnedas*returnColumnSizesarray.*Note:Bothreturnedarrayand*columnSizesarraymustbemalloced,assumecallercallsfree().*/voidbacktr......
  • 通用的CRUD之MongoDB
    前言这是一个简便的,对MongoDB增删改查,无需提前建库,建表,安装就能快速上手使用。MongoDB多条件查询需要JSON的多层嵌套如{DDATE:{$gte:{$date:'2023-06-05T13:41'},$lte:{$date:'2023-06-05T23:59'}},Qty:{$gt:10}},书写时非常难受,还容易出错。本类库支持类SQL查询语法,如"DDATE>......