共计 1408 个字符,预计需要花费 4 分钟才能阅读完成。
在 Go
中,for range
用来遍历 slice, map, chan
等,使用频率很高,但遍历虽好用,却很容易踩坑,且看 demo
如下:
1. 只有一个返回值时,则第一个参数是 index
package main | |
import "fmt" | |
func main() {s := []string{"a", "b", "c"} | |
// 只有一个返回值:则第一个参数是 index | |
for v := range s {fmt.Println(v) | |
} | |
// 两个返回值 | |
for i, v := range s {fmt.Println(i, v) | |
} | |
} |
输出结果如下:
0 | |
1 | |
2 | |
============= | |
0 a | |
1 b | |
2 c |
2. 遍历 map
为随机序输出,slice
为索引序输出
package main | |
import "fmt" | |
func main() {m := make(map[string]string) | |
m["aaa"] = "AAA" | |
m["bbb"] = "BBB" | |
m["ccc"] = "CCC" | |
m["ddd"] = "DDD" | |
m["eee"] = "EEE" | |
// range map 为随机序输出 | |
for i, v := range m {fmt.Println(i, v) | |
} | |
fmt.Println("==================") | |
s := []string{"aaa", "bbb", "ccc", "ddd", "eee"} | |
// range slice 为索引序输出 | |
for i, v := range s {fmt.Println(i, v) | |
} | |
} |
输出结果如下:
ddd DDD | |
eee EEE | |
aaa AAA | |
bbb BBB | |
ccc CCC | |
================== | |
0 aaa | |
1 bbb | |
2 ccc | |
3 ddd | |
4 eee |
3. range v 是值拷贝,且只会声明初始化一次
package main | |
import "fmt" | |
func main() {ParseStudent() | |
} | |
type student struct { | |
Name string | |
Age int | |
} | |
func ParseStudent() {m := make(map[string]*student) | |
stus := []student{{Name: "zhang", Age: 22}, | |
{Name: "li", Age: 23}, | |
{Name: "wang", Age: 24}, | |
} | |
for _, stu := range stus { | |
// 都指向了同一个 stu 的内存指针,为什么?// 因为 for range 中的 v 只会声明初始化一次 | |
// 不会每次循环都初始化,最后赋值会覆盖前面的 | |
fmt.Printf("%p\n", &stu) | |
// 1. bad | |
m[stu.Name] = &stu | |
// 2. good | |
/*newStu := stu | |
m[stu.Name] = &newStu*/ | |
} | |
for i, v := range m {fmt.Println(i, v) | |
} | |
} |
输出结果如下:
0xc00008e020 | |
0xc00008e020 | |
0xc00008e020 | |
zhang &{wang 24} | |
li &{wang 24} | |
wang &{wang 24} |
正确做法应该是:将 good
部分注释打开,运行结果如下:
0xc00000c080 | |
0xc00000c080 | |
0xc00000c080 | |
li &{li 23} | |
wang &{wang 24} | |
zhang &{zhang 22} |
<br/>
小结:for range
遍历注意以上几个小坑:
- 只有一个返回值时,则第一个参数是
index
; - 遍历
map
为随机序输出,slice
为索引序输出; -
range v
是值拷贝,且只会声明初始化一次;
掌握以上几点后,就让我们愉快的使用 range
吧~
正文完