Go 语言范围(Range)

range 在各种类型上的行为、值拷贝、以及修改元素的正确姿势。

Beginner
Key Points`for _, v := range s` 里的 `v` 是**副本**,改它不会改到切片元素。

各类型上的行为

TypeFirstSecond
数组 / 切片索引 int元素副本
字符串字节偏移 intrune
map键值副本
channel元素值无
整数(1.22+)从 0 开始无
函数(1.23+)每次调用返回值无
Go
// 整数 range(Go 1.22+)
for i := range 3 {
	fmt.Print(i, " ")
}
fmt.Println()
Output
0 1 2 

值拷贝的坑

Go
type Item struct{ N int }

items := []Item{{1}, {2}, {3}}

// 错:改的是副本
for _, it := range items {
	it.N *= 10
}
fmt.Println(items)

// 对:按下标改
for i := range items {
	items[i].N *= 10
}
fmt.Println(items)
Output
[{1} {2} {3}]
[{10} {20} {30}]

If elements are pointers ([]*Item), the copy still points at the same object, so it.N = ... works. Relying on that distinction hurts readability — prefer indexing.

map 的顺序

Go
m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4}

for k, v := range m {
	fmt.Print(k, v, " ")
}
fmt.Println()

// 需要固定顺序就先排序键
keys := slices.Sorted(maps.Keys(m))
for _, k := range keys {
	fmt.Print(k, m[k], " ")
}
fmt.Println()
Output
c 3 a 1 d 4 b 2 
a 1 b 2 c 3 d 4 
Go deliberately randomises map iteration order to stop programs depending on it. "Passes locally, fails intermittently in production" plus map iteration is almost always this.

遍历时修改

Go
m := map[string]int{"a": 1, "b": 2, "c": 3}

// 遍历中删除:安全
for k := range m {
	if k == "b" {
		delete(m, k)
	}
}
fmt.Println(len(m))

// 遍历中新增:新元素可能被遍历到,也可能不被
// 行为未定义,不要依赖
Output
2

提前退出

Go
found := false
for i, v := range []int{3, 1, 4, 1, 5} {
	if v == 4 {
		fmt.Println("在下标", i)
		found = true
		break
	}
}
fmt.Println(found)
Output
在下标 2
true

Go's range has no return false early-exit like forEach. Use slices.Index to find and slices.DeleteFunc to filter — clearer than hand-written loops.

Friends & Partners