首页后端开发GOGo 语言 for-range 的两个坑,你踩过吗?

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

时间2023-10-21 15:40:03发布访客分类GO浏览895
导读:坑一:迭代时协程引用索引和值先看看下面的例子,你知道最终输出的结果是什么吗?package main import ( "fmt" "time" func main( { var m = []int{1, 3, 5} fo...

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

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

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/

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Go 语言 for-range 的两个坑,你踩过吗?
本文地址: https://pptw.com/jishu/504620.html
Node.js 模块系统的原理、使用方式和一些常见的应用场景 golang腾讯云cos拷贝与删除

游客 回复需填写必要信息