range遍历切片时v是值副本,修改不影响原切片;遍历map顺序随机,需先排序key才能有序;遍历channel会阻塞至关闭,关闭后自动退出。
在 for _, v := range slice 中,v 每次迭代都会被重新赋值为当前元素的拷贝。修改 v 不会影响原切片内容:
nums := []int{1, 2, 3}
for _, v := range nums {
v = v * 10 // 这行无效:只改了 v 的副本
}
// nums 仍是 [1, 2, 3]若需修改原切片,必须通过索引访问:
for i := range slice 或 for i, _ := range slice
slice[i] = ...
Go 规范明确要求 range 遍历 map 的顺序是随机的——这不是 bug,而是设计选择,用于防止程序依赖隐式顺序。
常见误判场景:
range map 构造 JSON 或日志输出,期望键有序,结果每次不同若需有序遍历,得先提取 key 到切片并排序:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}for v := range ch 是典型的消费模式,但行为容易被低估:
for 会永久阻塞(goroutine 泄漏风险)break 提前退出后再从同一 channel 继续 range —— 已关闭的 channel 无法重开安全做法:
close(ch),且所有发送完成后再关ran
ge(竞态)select { case v, ok :=
Go 字符串底层是 UTF-8 字节数组,但 range 会自动解码为 Unicode 码点(rune):
s := "你好"
for i, r := range s {
fmt.Printf("index=%d, rune=%U\n", i, r)
// 输出:
// index=0, rune=U+4F60
// index=3, rune=U+597D
}注意:i 是字节偏移,不是 rune 索引;r 是 rune 类型,不是 byte。
utf8.RuneCountInString(s)
s[:n],要用 []rune(s)[:n] 再转回 stringrange 当作 byte 遍历,处理 ASCII 没问题,一遇到中文就索引错位实际写 range 循环时,最常忽略的是「谁拥有数据所有权」和「谁控制生命周期」——切片的底层数组、map 的并发安全性、channel 的关闭时机、字符串的编码边界,都藏在看似简单的语法糖下面。