Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go 语言 for 和 range 的实现 · /golang-for-range #141

Closed
draveness opened this issue Mar 17, 2019 · 48 comments
Closed

Go 语言 for 和 range 的实现 · /golang-for-range #141

draveness opened this issue Mar 17, 2019 · 48 comments

Comments

@draveness
Copy link
Owner

draveness commented Mar 17, 2019

https://draveness.me/golang-for-range

@741369
Copy link

741369 commented Mar 19, 2019

讲得真深奥,看一次好多点都没看懂

@draveness
Copy link
Owner Author

@741369
讲得真深奥,看一次好多点都没看懂

因为上下文的关联其实比较多,有什么疑惑可以问一下

@Hawken94
Copy link

Hawken94 commented Apr 1, 2019

讲得很好,也很深奥,收藏一波以后慢慢回顾

@Devying
Copy link

Devying commented Apr 7, 2019

这是得有很好的C基础才能看的懂呀。犟得很好,但是我看不懂😂

@draveness
Copy link
Owner Author

@Devying
这是得有很好的C基础才能看的懂呀。犟得很好,但是我看不懂😂

这其实都是 Go 语言啊

@smallersoup
Copy link

写的真好,学习了,大佬,可以弄个RSS吗,方便大家订阅,公众号已关注

@draveness
Copy link
Owner Author

写的真好,学习了,大佬,可以弄个RSS吗,方便大家订阅,公众号已关注

看下这个 https://draveness.me/feed.xml

@YangKian
Copy link

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

func Test(t *testing.T) {
	var m = map[string]int {
		"A": 21,
		"B": 22,
		"C": 23,
	}
    
	counter := 0
	for k, v := range m {
		if counter == 0 {
			delete(m, "A")
		}
		counter++
	}
	fmt.Println(counter)
}

这样一段代码输出的结果可能有两种:counter is 2 或者 counter is 3

您在讲解使用 for...range 遍历数组和切片时提到:

对于所有的 range 循环,Go 语言都会在编译期间将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中其实就发生了拷贝,所以我们遍历的切片其实已经不是原有的切片变量了。

对于 map,根据您给出的编译转换后的循环,也执行了 ha := a 这样一次拷贝,那应该后续遍历的 map 也不是原来的 map 了?如果这样来想的话上面那段代码的结果应该只会是 counter is 3 才对,为什么还会有 counter is 2 这种情况呢?

@YangKian
Copy link

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

我好像明白了:
func mapiterinit(t *maptype, h *hmap, it *hiter) {}
这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

@draveness
Copy link
Owner Author

这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

可以的,给你点赞,我刚想给你回复

@YangKian
Copy link

哈哈哈感谢大佬的教程,写的真的棒,受益匪浅!

@draveness draveness changed the title Go 语言 for 和 range 的实现 Go 语言 for 和 range 的实现 · /golang-for-range Feb 4, 2020
@dickgang
Copy link

dickgang commented Mar 5, 2020

你好,写得真不错,想问一下,这里的range实现,看下来就是在原来的函数里面插入一些控制变量,转换成普通的for函数吧,比较奇怪的是增加的变量是怎么命名的,会不会和用户自己定义的变量冲突了。

@draveness
Copy link
Owner Author

你好,写得真不错,想问一下,这里的range实现,看下来就是在原来的函数里面插入一些控制变量,转换成普通的for函数吧,比较奇怪的是增加的变量是怎么命名的,会不会和用户自己定义的变量冲突了。

编译器会通过 temp 函数生成一个唯一的标识符,你可以看看

@dickgang
Copy link

dickgang commented Mar 6, 2020

@draveness 谢谢这么快的回复,看了代码,确实是用这个函数来生成临时变量的。

// autotmpname returns the name for an autotmp variable numbered n.
func autotmpname(n int) string {
	// Give each tmp a different name so that they can be registerized.
	// Add a preceding . to avoid clashing with legal names.
	const prefix = ".autotmp_"
	// Start with a buffer big enough to hold a large n.
	b := []byte(prefix + "      ")[:len(prefix)]
	b = strconv.AppendInt(b, int64(n), 10)
	return types.InternString(b)
}

@yanjinbin
Copy link

yanjinbin commented Mar 30, 2020

循环中使用的这个变量 v2 会在每一次迭代被重新赋值,在赋值时也发生了拷贝

这段话 我读了5 6遍 才读出 其中意思

如果加2个字,是不是更好呢? 这个变量 v2 会在每一次迭代被重新赋值而覆盖

经常来看看博主的文章,受益良多!

@draveness
Copy link
Owner Author

循环中使用的这个变量 v2 会在每一次迭代被重新赋值,在赋值时也发生了拷贝

这段话 我读了5 6遍 才读出 其中意思

如果加2个字,是不是更好呢? 这个变量 v2 会在每一次迭代被重新赋值而覆盖

经常来看看博主的文章,受益良多!

我来加一下

@xyctruth
Copy link

xyctruth commented Apr 23, 2020

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

这里 v2是不是应该在for循环外定义?

@draveness
Copy link
Owner Author

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

@xyctruth
Copy link

@draveness

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

@xyctruth
Copy link

xyctruth commented Apr 23, 2020

@draveness

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

引用你上一个神奇的指针的例子,如果v2在循环内定义赋值的话,每次apend的&v2 都是不同的。并不会出现333。


2020-10-19 UPDATES: 已修复变量初始化位置

@draveness
Copy link
Owner Author

@xyctruth

@draveness

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

引用你上一个神奇的指针的例子,如果v2在循环内定义赋值的话,每次apend的&v2 都是不同的。并不会出现333。

确实是这样的,我回头改一下

@weaming
Copy link

weaming commented May 6, 2020

Left 不是「中止条件」,是继续条件


2020-05-06 UPDATES: 已修复

@dilllay
Copy link

dilllay commented Jun 9, 2020

不瞒您说,我确实也在看神奇的指针这个案例的时候看蒙圈了,直到看完解释也没理解。同一句话读了5,6次也没想明白,最后我自己把地址打出来,一切谜团才能解开。。。原谅我的迟钝(手动滑稽)

func main() {
	arr := []int{1, 2, 3}
	newArr := []*int{}
	for _, v := range arr {
		fmt.Println("")
		fmt.Printf("origin addr: %p value: %v", &v, v)
		newArr = append(newArr, &v)
	}
	for _, s := range newArr {
		fmt.Println("")
		fmt.Printf("addr: %p  value: %v", s, *s)
	}
}

输出结果:

origin addr: 0xc0000a67a8 value: 1
origin addr: 0xc0000a67a8 value: 2
origin addr: 0xc0000a67a8 value: 3
addr: 0xc0000a67a8  value: 3
addr: 0xc0000a67a8  value: 3
addr: 0xc0000a67a8  value: 3

@loveleicheng
Copy link

@YangKian

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

我好像明白了:
func mapiterinit(t *maptype, h *hmap, it *hiter) {}
这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

什么情况下会是2啊?为啥我跑了几次都是3?

@draveness
Copy link
Owner Author

@loveleicheng

@YangKian

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

我好像明白了:
func mapiterinit(t *maptype, h *hmap, it *hiter) {}
这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

什么情况下会是2啊?为啥我跑了几次都是3?

你跑的是哪段代码?

@loveleicheng
Copy link

@draveness

@loveleicheng

@YangKian

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

我好像明白了:
func mapiterinit(t *maptype, h *hmap, it *hiter) {}
这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

什么情况下会是2啊?为啥我跑了几次都是3?

你跑的是哪段代码?

就前面这哥们说的counter is 3的代码,大佬你一追问,我又找来重新跑了一下,一下就出现2的情况,没问题了,打扰。

@944603194
Copy link

@draveness

@xyctruth

@draveness

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

引用你上一个神奇的指针的例子,如果v2在循环内定义赋值的话,每次apend的&v2 都是不同的。并不会出现333。

确实是这样的,我回头改一下

为什么“v2在循环内定义赋值的话,每次apend的&v2 都是不同的”,这里即使每次从栈上分配v2,&v2地址也会是相同的吧

@mumu0125
Copy link

不知道python有没有类似的文档,讲的很好,但是真的很难看懂

@woofyzhao
Copy link

woofyzhao commented Sep 20, 2020

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里其实也解释了"循环永动机"现象,建议行文可以显示地点一下。


2020-10-26 UPDATES: 已添加

@MaxwellZY
Copy link

MaxwellZY commented Oct 25, 2020

ha := s
for hv1 := 0; hv1 < len(ha); {
    hv1t := hv1
    hv2 := rune(ha[hv1])
    if hv2 < utf8.RuneSelf {
        hv1++
    } else {
        hv2, hv1 = decoderune(h1, hv1)
    }
    v1, v2 = hv1t, hv2
}

关于字符串这段的描述,decoderune的第一个参数应该是ha吧?


2020-10-26 UPDATES: 已修复

index 551f269..683d6fd 100644
--- a/design-and-implementation-of-go/content/docs/part2-foundation/ch05-keyword/golang-for-range.md
+++ b/design-and-implementation-of-go/content/docs/part2-foundation/ch05-keyword/golang-for-range.md
@@ -499,7 +499,7 @@ for hv1 := 0; hv1 < len(ha); {
     if hv2 < utf8.RuneSelf {
         hv1++
     } else {
-        hv2, hv1 = decoderune(h1, hv1)
+        hv2, hv1 = decoderune(ha, hv1)
     }
     v1, v2 = hv1t, hv2
 }

@draveness
Copy link
Owner Author

@MaxwellZY

ha := s
for hv1 := 0; hv1 < len(ha); {
   hv1t := hv1
   hv2 := rune(ha[hv1])
   if hv2 < utf8.RuneSelf {
       hv1++
   } else {
       hv2, hv1 = decoderune(h1, hv1)
   }
   v1, v2 = hv1t, hv2
}

关于字符串这段的描述,decoderune的第一个参数应该是ha吧?

为什么觉得是 ha 呢,ha 只是原有字符串的拷贝

@MaxwellZY
Copy link

@draveness

@MaxwellZY

ha := s
for hv1 := 0; hv1 < len(ha); {
   hv1t := hv1
   hv2 := rune(ha[hv1])
   if hv2 < utf8.RuneSelf {
       hv1++
   } else {
       hv2, hv1 = decoderune(h1, hv1)
   }
   v1, v2 = hv1t, hv2
}

关于字符串这段的描述,decoderune的第一个参数应该是ha吧?

为什么觉得是 ha 呢,ha 只是原有字符串的拷贝

首先在给定的这段代码中h1是个未定义的变量,表述上有问题
其次,decoderune的功能不就是从给定的字符串和偏移返回非ASCII rune么,所以传ha没错啊

@draveness
Copy link
Owner Author

@MaxwellZY 首先在给定的这段代码中h1是个未定义的变量,表述上有问题

这段没看懂

其次,decoderune的功能不就是从给定的字符串和偏移返回非ASCII rune么,所以传ha没错啊

重新看了下,确实是这样的,已经改了

@kilosliu
Copy link

kilosliu commented Nov 1, 2020

我觉得循环永动机那个位置应该不是因为遍历的拷贝所以才会不出现循环永动机,毕竟切片是引用对象,而是因为他直接对拷贝后的数组对象求了len,然后循环终止的条件是小于一开始求的len,像如下代码的话那么就会出现索引越界panic,因为他的终止条件是<len=3

arr := []int{1, 2, 3}
	for i :=  range arr {
		fmt.Println(arr[i])
		arr = arr[:2]
	}

2020-11-06 UPDATES: 已修复

@draveness
Copy link
Owner Author

@Akatsukizzz 我觉得循环永动机那个位置应该不是因为遍历的拷贝所以才会不出现循环永动机,毕竟切片是引用对象,而是因为他直接对拷贝后的数组对象求了len,然后循环终止的条件是小于一开始求的len,像如下代码的话那么就会出现索引越界panic,因为他的终止条件是<len=3

arr := []int{1, 2, 3}
	for i :=  range arr {
		fmt.Println(arr[i])
		arr = arr[:2]
	}

你说的有道理,后面会改一下

@kilosliu
Copy link

kilosliu commented Nov 8, 2020

博主你好,最近重新看的时候用之前评论的代码改动了一下

func main() {
	var m = map[string]int{
		"A": 21,
		"B": 22,
		"C": 23,
	}

	counter := 0
	for k, v := range m {
		if counter == 0 {
			m["D"] = 20
		}
		counter++
		fmt.Println(k, v)
	}
	fmt.Println(counter)
}

最终结果有时是4可以遍历到D有时候是3没有遍历到D,这是为什么呢,感觉哈希表的遍历过程有点不好理解,是不是有可能是一开始的startBucket是空桶然后将D插入到了这个空桶所以就没有遍历到

@draveness
Copy link
Owner Author

draveness commented Nov 10, 2020

博主你好,最近重新看的时候用之前评论的代码改动了一下

func main() {
	var m = map[string]int{
		"A": 21,
		"B": 22,
		"C": 23,
	}

	counter := 0
	for k, v := range m {
		if counter == 0 {
			m["D"] = 20
		}
		counter++
		fmt.Println(k, v)
	}
	fmt.Println(counter)
}

最终结果有时是4可以遍历到D有时候是3没有遍历到D,这是为什么呢,感觉哈希表的遍历过程有点不好理解,是不是有可能是一开始的startBucket是空桶然后将D插入到了这个空桶所以就没有遍历到

我在本地和 Go Playground 测试了几次都可以遍历到 D https://play.golang.org/p/fSPpo4_-k57

@kilosliu
Copy link

kilosliu commented Nov 10, 2020

@draveness

博主你好,最近重新看的时候用之前评论的代码改动了一下

func main() {
	var m = map[string]int{
		"A": 21,
		"B": 22,
		"C": 23,
	}

	counter := 0
	for k, v := range m {
		if counter == 0 {
			m["D"] = 20
		}
		counter++
		fmt.Println(k, v)
	}
	fmt.Println(counter)
}

最终结果有时是4可以遍历到D有时候是3没有遍历到D,这是为什么呢,感觉哈希表的遍历过程有点不好理解,是不是有可能是一开始的startBucket是空桶然后将D插入到了这个空桶所以就没有遍历到

我在本地和 Go Playground 测试了几次都可以遍历到 D https://play.golang.org/p/fSPpo4_-k57

我测试的时候也是大部分时候可以遍历到D,但仍然有时候遍历不到,我刚刚用playground也试过了有遍历不到D的情况,就我猜是不是有可能一开始选择的遍历桶startBucket可能是个空桶,然后遍历过去之后将D插入到了已经遍历过的桶中,所以就有可能会出现遍历不到D的情况

@kilosliu
Copy link

@draveness

@xyctruth

@draveness

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
for ; hv1 < hn; hv1++ {
    tmp := ha[hv1]
    v1, v2 := hv1, tmp. 
    ...
}

对于所有的 range 循环,Go 语言都会在编译期将原切片或者数组赋值给一个新的变量 ha,在赋值的过程中就发生了拷贝,所以我们遍历的切片已经不是原始的切片变量了。

这里 v2是不是应该在for循环外定义?

说下你的理由?

而遇到这种同时遍历索引和元素的 range 循环时,Go 语言会额外创建一个新的 v2 变量存储切片中的元素,循环中使用的这个变量 v2 会在每一次迭代被重新赋值而覆盖,在赋值时也发生了拷贝。

引用你上一个神奇的指针的例子,如果v2在循环内定义赋值的话,每次apend的&v2 都是不同的。并不会出现333。

确实是这样的,我回头改一下

同时不光是切片的range遍历,哈希表、通道和字符串的遍历的元素应该都是在外层循环定义,形如 for i, v := range map/string {} 给我们使用的那个v如果打印地址的话应该都是不变的,如果在循环内部将这个v用 := 赋值给了另一个变量那么就不会出现打印的地址都是同一个了

@GaryLiu1996
Copy link

不看十遍对不起大佬这么细致的讲解:D 太精彩了

@Imfan
Copy link

Imfan commented Jan 12, 2021

突然感觉 for range 对Map的遍历,意义不大。。。

@draveness
Copy link
Owner Author

突然感觉 for range 对Map的遍历,意义不大。。。

不太明白你在这里提出的问题

@Imfan
Copy link

Imfan commented Jan 12, 2021

@draveness

突然感觉 for range 对Map的遍历,意义不大。。。

不太明白你在这里提出的问题

就是感觉 for range 有好多坑,不如用 for 循环

@dillanzhou
Copy link

dillanzhou commented Jan 26, 2021

最后的通道部分,“如果存在当前值,会为 v1 赋值并清除 hv1 变量中的数据,然后重新陷入阻塞等待新数据;”,这里特别强调清除hv1变量中的数据有什么特别的意义吗?

又看了一下walkrange的实现还真有注释说明这个清除hv1的意义(issue15281),是为了blocking在chan上的时候可以回收掉上一个获取的变量。只在chan里传递的变量类型占空间特别大时才有意义。

@herac1es
Copy link

@kilosliu

@draveness

博主你好,最近重新看的时候用之前评论的代码改动了一下

func main() {
	var m = map[string]int{
		"A": 21,
		"B": 22,
		"C": 23,
	}

	counter := 0
	for k, v := range m {
		if counter == 0 {
			m["D"] = 20
		}
		counter++
		fmt.Println(k, v)
	}
	fmt.Println(counter)
}

最终结果有时是4可以遍历到D有时候是3没有遍历到D,这是为什么呢,感觉哈希表的遍历过程有点不好理解,是不是有可能是一开始的startBucket是空桶然后将D插入到了这个空桶所以就没有遍历到

我在本地和 Go Playground 测试了几次都可以遍历到 D https://play.golang.org/p/fSPpo4_-k57

我测试的时候也是大部分时候可以遍历到D,但仍然有时候遍历不到,我刚刚用playground也试过了有遍历不到D的情况,就我猜是不是有可能一开始选择的遍历桶startBucket可能是个空桶,然后遍历过去之后将D插入到了已经遍历过的桶中,所以就有可能会出现遍历不到D的情况

我试了下确实也会出现遍历不到D,我觉得你说的这个原因是有可能的,希望博主能求证下

@llwwbb
Copy link

llwwbb commented Aug 9, 2021

@Imfan

@draveness

突然感觉 for range 对Map的遍历,意义不大。。。

不太明白你在这里提出的问题

就是感觉 for range 有好多坑,不如用 for 循环

但是不用range没法遍历map吧?用反射?

@llwwbb
Copy link

llwwbb commented Aug 9, 2021

@YangKian

您好,冒昧打扰,在看 for 和 range 的实现 这章的时候遇到一个问题想跟您请教,代码如下:

我好像明白了:
func mapiterinit(t *maptype, h *hmap, it *hiter) {}
这个函数的第二个参数是指针,根据调用语句 mapiterinit(typename(t), ha, &hit) 推断 ha := a 这个拷贝的是指针,所以后续对 map 的修改还是会影响到原来的 map,之前傻了没注意这里😂

所以map的本质不是像slice那样的SliceHeader而是hmap的指针?

@cuiweixie
Copy link

最近跟朋友讨论了下 for k, v := range a 的实现,a是slice or array的时候,要复制这个行为是不是lang spec定义的行为?
lang spec 有一段是这样描述的:
The range expression
x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and 
len(x) is constant, the range expression is not evaluated.
向官方提问后的反馈(和我给朋友的反馈是一样的,哈哈):
https://groups.google.com/g/golang-nuts/c/LEEb0K4lih4
提两个建议:

  1. 咱们的文章还可以描述下copy这个操作是有可能被后面的ssa pass优化掉的
  2. 描述编译器的实现时也可以描述下lang spec的要求是怎么样,哪些必须遵从,哪些编译器是可以优化的

Repository owner locked and limited conversation to collaborators Mar 21, 2022
@draveness draveness converted this issue into discussion #557 Mar 21, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests