range 必须依附于 for 语句,是其第三种形式(遍历型)的可选子句,不能独立使用;遍历时 v 是副本,修改不影响原数据,需用索引或赋值更新。

for 循环和 range 不是并列关系,而是绑定关系——range 必须跟在 for 后面,它不是独立语法,只是 for 的一种子句形式。
为什么不能单独写 range?
Go 语言中没有 range 语句,只有 for 语句;range 是 for 的可选子句(RangeClause),语法上必须依附于 for。试图写 range arr 或 if x := range m { ... } 都会报错:syntax error: unexpected range, expecting {。
-
for i, v := range arr合法 ——range是for的一部分 -
range arr非法 —— 编译器根本无法解析 -
switch range m非法 ——range只能用于for
for 的三种写法,range 只属于其中一种
Go 的 for 有且仅有三种合法形态,range 仅出现在第三种(集合遍历型)中:
-
for init; condition; post:C 风格计数循环,如for i := 0; i -
for condition:while 风格,如for n > 0 { n-- } -
for key, val := range x:唯一支持range的形式,x必须是数组、切片、字符串、map 或 channel
注意:for range x(省略 key/val)是合法的,但依然属于第三种形式;它不返回值,只做遍历动作,常用于触发副作用(如关闭资源、发送信号)。
立即学习“go语言免费学习笔记(深入)”;
遍历切片时,range 返回的是副本,不是引用
这是最常被忽略的陷阱:for _, v := range slice 中的 v 是每个元素的拷贝。修改 v 不会影响原切片:
nums := []int{1, 2, 3}
for _, v := range nums {
v *= 10 // 这里改的是 v 的副本
}
// nums 仍是 [1, 2, 3],没变要真正修改元素,必须用索引:
- 写成
for i := range nums { nums[i] *= 10 } - 或
for i, v := range nums { nums[i] = v * 10 }
对 map 和 channel 也适用类似逻辑:map 的 v 是 value 副本,channel 的 v 是接收值副本。
性能差异真实存在,但别过早优化
在极端场景(如百万级 JSON 数组解析)下,手写 for i := 0; i 比 <code>for _, v := range arr 快约 25–30%,内存占用更低。但这仅在 jsoniter 等底层解析器中体现明显。
- 日常业务代码中,二者性能差距通常在纳秒级,不可测量
-
range更安全(自动处理空切片、避免越界)、更易读、更符合 Go 风格 - 除非压测明确显示循环成为瓶颈,否则优先用
range
真正影响性能的,往往是循环体内的逻辑,而不是遍历方式本身。


















