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

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

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/

Go 语言 for-range 的两个坑,你踩过吗?的更多相关文章

  1. go语言之range

    Go 语言范围(Range) Go 语言中 range 关键字用于 for 循环中迭代数组(array).切片(slice).通道(channel)或集合(map)的元素.在数组和切片中它返回元素的索 ...

  2. 两个坑-Linux下Network-Manager有线未托管-DNS resolv.conf文件开机被清空

    Linux里面有两套管理网络连接的方案: 1./etc/network/interfaces(/etc/init.d/networking) 2.Network-Manager 两套方案是冲突的,不能 ...

  3. iscroll遇到的两个坑

    最近移动端闪付遇到的两个坑做下总结: 1.使用iscroll后,滑动并没有生效 解决方案: 首先要查看:结构是否正确: <div id="wrapper">   //w ...

  4. R语言·文本挖掘︱Rwordseg/rJava两包的安装(安到吐血)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- R语言·文本挖掘︱Rwordseg/rJava ...

  5. 记自己在spring中使用redis遇到的两个坑

    本人在spring中使用redis作为缓存时,遇到两个坑,现在记录如下,算是作为自己的备忘吧,文笔不好,望大家见谅: 一.配置文件 <!-- 加载Properties文件 --> < ...

  6. MySql 5.7安装(随机密码,修改默认密码)两个坑

    MySql 5.7安装(随机密码,修改默认密 下载了MySql 最新版本,安装的过程中,发现了很多新特性 1.data目录不见了 在进行my-default.ini配置的时候 (需要配置 # base ...

  7. Go语言结构体转json的坑

    Go语言结构体转json的坑 标签(空格分隔): go json.Marshal() JSON输出的时候必须注意,只有导出的字段(首字母是大写)才会被输出,如果修改字段名,那么就会发现什么都不会输出, ...

  8. 论nw.js的坑~~~感觉我所有的前端能遇到的坑都踩了一遍

    先总结:nw.js 真特么的...难用...文档,我得先百度才能看的稍微明白点文档......!!!!!!我感觉我所有的前端能遇到的坑都踩了一遍,此文针对前后端分离项目,别的先不说 一.不需要在项目里 ...

  9. go语言---for range

    学习-go语言坑之for range https://www.cnblogs.com/hetonghai/p/6718250.html go只提供了一种循环方式,即for循环,在使用时可以像c那样使用 ...

  10. 【转】C语言宏定义的几个坑和特殊用法

    总结一下C语言中宏的一些特殊用法和几个容易踩的坑.由于本文主要参考GCC文档,某些细节(如宏参数中的空格是否处理之类)在别的编译器可能有细微差别,请参考相应文档. 宏基础 宏仅仅是在C预处理阶段的一种 ...

随机推荐

  1. vue项目中使用lottie动画

    一,安装vue-lottie 1 npm install --save vue-lottie 二,引用vue-lottie 在main.js引入并全局注册组件也可在页面单独引用 1 import lo ...

  2. JavaFx 生成二维码工具类封装

    原文地址: JavaFx 生成二维码工具类封装 - Stars-One的杂货小窝 之前星之音乐下载器有需要生成二维码功能,当时用的是一个开源库来实现的,但是没过多久,发现那个库依赖太多,有个http- ...

  3. 2020-09-14:KVM和XEN虚拟化的区别?

    福哥答案2020-09-14:#福大大架构师每日一题#[答案来自此链接](https://bbs.csdn.net/topics/397671000)KVM:1.虚拟化支持:全虚拟化.2.支持架构:虚 ...

  4. 2022-05-20:给定一个正数数组arr,长度为N,依次代表N个任务的难度,给定一个正数k, 你只能从0任务开始,依次处理到N-1号任务结束,就是一定要从左往右处理任务, 只不过,难度差距绝对值不

    2022-05-20:给定一个正数数组arr,长度为N,依次代表N个任务的难度,给定一个正数k, 你只能从0任务开始,依次处理到N-1号任务结束,就是一定要从左往右处理任务, 只不过,难度差距绝对值不 ...

  5. Spring Boot 配置文件总结

    前言 Spring Boot 中提供一个全局的配置文件:application.properties,这个配置文件的作用就是,允许我们通过这个配置文件去修改 Spring Boot 自动配置的默认值. ...

  6. 一个.Net开发的功能强大、易于使用的流媒体服务器和管理系统

    推荐一个视频管理系统,非常适合个人或者公司打造视频网站. 项目简介 这是基于.Net Core开发的,跨平台的开源项目:支持多种音视频格式,如MP3.MP4.AVI.WMV.FLV等:支持本地管理与远 ...

  7. AcWing 1019. 庆功会

    为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员. 期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力. 输入格式 第一行二个数n,m,其中n代表 ...

  8. 代码随想录算法训练营Day17二叉树|110.平衡二叉树  257. 二叉树的所有路径 404.左叶子之和

    优先掌握递归 110.平衡二叉树 题目链接:110.平衡二叉树 给定一个二叉树,判断它是否是高度平衡的二叉树. 本题中,一棵高度平衡二叉树定义为: 一个二叉树_每个节点_ 的左右两个子树的高度差的绝对 ...

  9. 2023-05-27:给你一个只包含小写英文字母的字符串 s 。 每一次 操作 ,你可以选择 s 中两个 相邻 的字符,并将它们交换。 请你返回将 s 变成回文串的 最少操作次数 。 注意 ,输入数据

    2023-05-27:给你一个只包含小写英文字母的字符串 s . 每一次 操作 ,你可以选择 s 中两个 相邻 的字符,并将它们交换. 请你返回将 s 变成回文串的 最少操作次数 . 注意 ,输入数据 ...

  10. How to boot the Raspberry Pi system from a USB Mass Storage Device All In One

    How to boot the Raspberry Pi system from a USB Mass Storage Device All In One 如何从 USB 启动树莓派引导系统 / 如何 ...