range遍历切片时v是值副本,修改不影响原切片;遍历map顺序随机,需先排序key才能有序;遍历channel会阻塞至关闭,关闭后自动退出。

range 遍历切片时,v 是值的副本,不是引用
在 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] = ... - 注意:如果切片底层数组被扩容(如 append 导致扩容),后续索引可能失效
range 遍历 map 时,遍历顺序不保证且每次不同
Go 规范明确要求 range 遍历 map 的顺序是随机的——这不是 bug,而是设计选择,用于防止程序依赖隐式顺序。
常见误判场景:
立即学习“go语言免费学习笔记(深入)”;
- 测试中偶然看到固定顺序,误以为“稳定”,上线后出错
- 用
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])
}range 遍历 channel 会阻塞直到有数据或 channel 关闭
for v := range ch 是典型的消费模式,但行为容易被低估:
- channel 未关闭且无数据时,该
for会永久阻塞(goroutine 泄漏风险) - channel 关闭后,循环自动退出,不会返回零值
- 不能用
break提前退出后再从同一 channel 继续range—— 已关闭的 channel 无法重开
安全做法:
- 确保有 goroutine 负责
close(ch),且所有发送完成后再关 - 避免在多个 goroutine 中对同一 channel 使用
range(竞态) - 若需非阻塞尝试,改用
select { case v, ok :=
range 在字符串上遍历的是 rune,不是 byte
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) - 想截取前 N 个字符?不能直接
s[:n],要用[]rune(s)[:n]再转回 string - 若误把
range当作 byte 遍历,处理 ASCII 没问题,一遇到中文就索引错位
实际写 range 循环时,最常忽略的是「谁拥有数据所有权」和「谁控制生命周期」——切片的底层数组、map 的并发安全性、channel 的关闭时机、字符串的编码边界,都藏在看似简单的语法糖下面。










