Go语言仅用for实现所有循环,其唯一语法for [init; cond; post]中三部分均可省略,可模拟for/while/无限循环;常见错误是照搬其他语言括号写法或边界处理不当。

Go 语言里没有 while、do-while,所有循环都靠一个 for 语句实现——但它比你想象中更灵活,也更容易写错边界。
Go 的 for 只有一种语法形式,但能模拟三种常见循环
Go 的 for 语句只有一种结构:for [init; cond; post],但 init、cond、post 全部可省略。这使得它既能当传统 for 用,也能当 while 甚至无限循环用。
常见误写是照搬其他语言习惯,比如写 for (i = 0; i —— Go 不允许括号,也不支持逗号分隔多条初始化语句。
- 传统计数循环:
for i := 0; i < len(slice); i++ { fmt.Println(slice[i]) } - while-like 循环(省略 init 和 post):
for count < maxTries { if try() { break } count++ } - 死循环(只留关键字):
for { select { case msg := <-ch: handle(msg) case <-time.After(1 * time.Second): continue } }(注意:这种必须配合break或return,否则会卡死)
range 遍历切片/数组/map/channel 时的陷阱
range 是 Go 中最常用的遍历方式,但它返回的是副本,不是引用;而且索引和值的顺序容易记混。
立即学习“go语言免费学习笔记(深入)”;
典型错误:想修改切片元素却改了局部变量;或在循环中把 range 返回的 value 地址存进切片,结果所有指针都指向最后一个元素。
- 遍历切片并修改原元素,必须用索引:
for i := range data { data[i] *= 2 // ✅ 正确 } // ❌ 错误写法(改的是副本) for _, v := range data { v *= 2 // data 不变 } - 取地址要小心:
var ptrs []*int for _, v := range []int{1, 2, 3} { ptrs = append(ptrs, &v) // ❌ 全是指向同一个 v 的地址 } // ✅ 正确:用索引取地址,或显式声明新变量 for i := range slice { ptrs = append(ptrs, &slice[i]) } - 遍历 map 无序,每次运行顺序可能不同;遍历 channel 会在关闭后自动退出。
for + break/continue 标号用法解决嵌套跳转
Go 不支持 break label 跨函数跳转,但支持给 for 加标号,用于跳出多层循环——这点常被忽略,导致用冗余标志变量“硬扛”逻辑。
标号必须紧贴 for 前面,且不能与变量同名(否则编译报错 label not defined)。
- 跳出外层循环:
outer: for i := 0; i < 3; i++ { for j := 0; j < 4; j++ { if i == 1 && j == 2 { break outer // 直接跳出 outer 循环 } fmt.Printf("i=%d,j=%d ", i, j) } } - continue 标号也有效:
loop: for i := 0; i < 5; i++ { if i == 2 { continue loop // 跳过本次,继续下一轮 i } fmt.Print(i) } - 标号不能跨函数、不能用于
if或switch,仅作用于for。
真正难的不是语法,而是理解 range 的值语义、标号的作用域、以及省略条件时的隐含行为——这些地方一不留神就引入静默 bug。










