
在 Go 中使用 range 遍历二维切片时,若直接对 range 变量赋值(如 y = make(...)),不会修改原切片元素,因为 range 提供的是元素副本而非引用,导致子切片未被初始化而引发 panic。
在 go 中使用 range 遍历二维切片时,若直接对 range 变量赋值(如 `y = make(...)`),不会修改原切片元素,因为 range 提供的是元素副本而非引用,导致子切片未被初始化而引发 panic。
Go 的 range 循环在遍历切片时,总是复制元素值,而非提供对底层数组或切片头结构的引用。这一点在处理二维切片(即 [][]T)时尤为关键:外层 range 返回的是每个内层切片([]T)的独立副本;对这个副本变量(如 y)重新赋值(y = make([]uint8, dx)),仅改变了该局部变量指向的新切片,并不会更新原始二维切片 pic[i] 的内容。因此,pic 中所有子切片仍为 nil 或零长度切片,后续访问 pic[i][j] 便会触发 index out of range panic。
✅ 正确做法:始终通过索引显式赋值
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for i := range pic { // i 是索引:0, 1, ..., dy-1
pic[i] = make([]uint8, dx) // 直接向 pic[i] 赋值新切片
for j := range pic[i] {
pic[i][j] = uint8(j*i + j) // 示例计算逻辑(如 Tour of Go 要求)
}
}
return pic
}❌ 错误写法解析(引发 panic 的原因):
for _, y := range pic { // y 是 pic[i] 的副本(初始为 nil 或空切片)
y = make([]uint8, dx) // ✖️ 仅修改局部变量 y,pic[i] 仍是 nil
for _, x := range y { // 此时 y 已非 nil,但 pic[i] 仍为空 → 后续使用 pic[i][j] 会 panic
x = uint8(1) // ✖️ 同样,x 是 y[j] 的副本,修改无效
}
}⚠️ 关键注意事项:
- range 的第二个返回值(如 y)是只读副本,不可用于反向更新原切片;
- 若需修改原切片元素,必须使用索引(slice[i] = ...)或指针(&slice[i]);
- 对于二维切片,外层 range 的索引用于定位行(pic[i]),内层 range 的索引用于定位列(pic[i][j]);
- 初始化二维切片时,务必先分配外层数组(make([][]T, rows)),再逐行分配内层切片(pic[i] = make([]T, cols))。
总结:Go 的值语义要求开发者明确区分“读取”与“写入”操作。range 是安全的遍历工具,但不是赋值捷径。坚持使用索引赋值,既能避免 panic,也符合 Go 清晰、显式的编程哲学。

















