编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

  • Go入门到精通

    • 入门教程

      • README
      • Go简史
      • 基础语法
      • 数据类型
      • 运算符
      • 复合类型
      • 流程语句
        • 目录介绍
        • 6.1 本章学习目标
        • 6.2 if 与 if-init
          • 6.2.1 没有括号的 condition
          • 6.2.2 if err := f(); err != nil 模式
          • 6.2.3 init 子句的作用域
        • 6.3 for 三种形态
          • 6.3.1 三段式 for i := 0; i < n; i++
          • 6.3.2 单条件 for cond 等价 while
          • 6.3.3 无条件 for {} 等价 while(true)
          • 6.3.4 for ... range 遍历
          • 6.3.5 Go 1.22:for i := range N 整数遍历
          • 6.3.6 Go 1.22:循环变量每轮新建
          • 6.3.7 Go 1.23:for range func 迭代器
        • 6.4 switch
          • 6.4.1 自带 break,不需要写
          • 6.4.2 fallthrough 显式穿透
          • 6.4.3 多值 case 与表达式 case
          • 6.4.4 类型 switch(预告,第 10 章详讲)
          • 6.4.5 无条件 switch 替代 if-else 链
        • 6.5 break / continue 与标签
        • 6.6 goto:什么时候才该用
        • 6.7 没有 while / do-while / 三元
        • 6.8 综合示例:FizzBuzz 与状态机
          • 示例 1:FizzBuzz(基础流程语句训练)
          • 示例 2:红绿灯状态机(标签 + switch)
        • 6.9 Go 新手陷阱 Top 5
          • ❌ 陷阱 1:switch 期望穿透到下一 case
          • ❌ 陷阱 2:break 跳不出嵌套循环
          • ❌ 陷阱 3:Go 1.21 及之前的循环变量陷阱
          • ❌ 陷阱 4:range 修改的是副本
          • ❌ 陷阱 5:for-range map 期望顺序稳定
        • 6.10 思考题
        • 6.11 推荐阅读
          • 卷内
          • 跨卷
          • 外部资料
      • 函数
      • 指针与逃逸
      • 结构体与方法
      • 接口与多态
      • 错误处理
      • 并发goroutine
      • 通道channel
      • 同步sync包
      • IO和文件
      • 标准库与泛型
      • 工程化与模块
      • 特性图谱
    • 综合案例

    • 专栏博客

    • 开发技巧

  • JavaScript入门

  • CodeX
  • Go入门到精通
  • 入门教程
杨充
2026-05-21
目录

流程语句

# 第 6 章 流程语句

Go 的流程控制:if-init、三种 for、switch 自带 break、goto 与标签、Go 1.22 新增 for i := range N、循环变量每轮新建。 关键词:if、for、switch、break/continue 标签、goto、fallthrough、循环变量


# 目录介绍

  • 6.1 本章学习目标
  • 6.2 if 与 if-init
    • 6.2.1 没有括号的 condition
    • 6.2.2 if err := f(); err != nil 模式
    • 6.2.3 init 子句的作用域
  • 6.3 for 三种形态
    • 6.3.1 三段式 for i := 0; i < n; i++
    • 6.3.2 单条件 for cond 等价 while
    • 6.3.3 无条件 for {} 等价 while(true)
    • 6.3.4 for ... range 遍历
    • 6.3.5 Go 1.22:for i := range N 整数遍历
    • 6.3.6 Go 1.22:循环变量每轮新建
    • 6.3.7 Go 1.23:for range func 迭代器
  • 6.4 switch
    • 6.4.1 自带 break,不需要写
    • 6.4.2 fallthrough 显式穿透
    • 6.4.3 多值 case 与表达式 case
    • 6.4.4 类型 switch(预告,第 10 章详讲)
    • 6.4.5 无条件 switch 替代 if-else 链
  • 6.5 break / continue 与标签
  • 6.6 goto:什么时候才该用
  • 6.7 没有 while / do-while / 三元
  • 6.8 综合示例:FizzBuzz 与状态机
  • 6.9 Go 新手陷阱 Top 5
  • 6.10 思考题
  • 6.11 推荐阅读

# 6.1 本章学习目标

  • ✅ 默写 for 的 5 种形态(三段式、单条件、无条件、range、range N)
  • ✅ 理解 Go 1.22 循环变量每轮新建的语义改动以及为什么"破坏 Go 1 兼容承诺"也要改
  • ✅ 能用 if init; cond 一行收窄变量作用域
  • ✅ 区分 Go 的 switch 与 C 的差异(默认不穿透、case 可任意类型、可裸 switch 替代 if-else)
  • ✅ 用带标签的 break/continue 控制嵌套循环
  • ✅ 理解为什么 Go 没有 while / do-while / 三元运算符

# 6.2 if 与 if-init

# 6.2.1 没有括号的 condition

if x > 0 {
    fmt.Println("positive")
} else if x < 0 {
    fmt.Println("negative")
} else {
    fmt.Println("zero")
}
1
2
3
4
5
6
7

强制规则:

规则 说明
condition 不要括号 if (x > 0) {} 多余括号会被 gofmt 保留但 lint 警告
{ 必须与 if 同行 if x > 0\n{ 编译错(Go 的 ASI 自动分号机制不允许)
condition 必须是 bool if 1 {}、if x = 0 {} 都编译错
else 必须紧跟 } 同行 } \n else { } 编译错

最后一条是 Go 比较"独裁"的地方——它直接在语法层杜绝了"if/else 中间塞代码"的奇怪写法。

# 6.2.2 if err := f(); err != nil 模式

Go 的 if 支持"初始化语句 + 条件"两段式:

if 初始化语句; 条件 {
    // ...
}
1
2
3

这是 Go 最有标志性的工程惯用法——错误处理的"短作用域"模式:

// ❌ 不推荐:err 泄露到外层作用域
err := doSomething()
if err != nil {
    return err
}
// 这里还能看到 err,容易被后续代码"复用"出 bug

// ✅ 推荐:err 只在 if 内部可见
if err := doSomething(); err != nil {
    return err
}
// 这里 err 已经不存在
1
2
3
4
5
6
7
8
9
10
11
12

类似地用于 map / 类型断言 / channel 接收:

if v, ok := m["key"]; ok {
    fmt.Println(v)
}

if u, ok := i.(*User); ok {
    fmt.Println(u.Name)
}

if msg, ok := <-ch; ok {
    fmt.Println(msg)
}
1
2
3
4
5
6
7
8
9
10
11

# 6.2.3 init 子句的作用域

init 子句声明的变量在整个 if-else 链里都可见:

if v := compute(); v > 100 {
    fmt.Println("big:", v)
} else if v > 10 {
    fmt.Println("medium:", v) // ✅ 还看得到 v
} else {
    fmt.Println("small:", v)  // ✅ 也看得到
}
// fmt.Println(v) // ❌ 这里看不到了
1
2
3
4
5
6
7
8

这种"块级作用域 + 自动失效"是 Go 控制流的核心设计,很多 C / Java 程序员一开始用不惯,写久了反而会觉得"特别舒服"——因为出 bug 的概率断崖式下降。


# 6.3 for 三种形态

Go 把 for、while、do-while、foreach 全部统一成一个关键字 for——从五种形态。

# 6.3.1 三段式 for i := 0; i < n; i++

for i := 0; i < 10; i++ {
    fmt.Println(i)
}
1
2
3

跟 C 一致,三个分号分隔的子句(init、cond、post)都可省略。

# 6.3.2 单条件 for cond 等价 while

i := 0
for i < 10 {  // 等价于 while (i < 10)
    fmt.Println(i)
    i++
}
1
2
3
4
5

# 6.3.3 无条件 for {} 等价 while(true)

for {
    msg, ok := <-ch
    if !ok {
        break
    }
    handle(msg)
}
1
2
3
4
5
6
7

事件循环、worker pool、长连接处理几乎全用这种形态。

# 6.3.4 for ... range 遍历

range 能遍历的 5 种类型:

类型 形式 第一返回值 第二返回值
array / slice for i, v := range s 索引 元素拷贝
string for i, r := range s 字节偏移(不是字符序号) rune(Unicode 码点)
map for k, v := range m key value
channel for v := range ch 接收到的值 (无)
整数(Go 1.22+) for i := range N 0..N-1 (无)
// slice:可以省略不需要的返回值
for _, v := range []int{10, 20, 30} {
    fmt.Println(v)
}

// 只要索引
for i := range nums { ... }

// channel 自动检测关闭:sender close(ch) 后 range 自然退出
for msg := range ch {
    handle(msg)
}
1
2
3
4
5
6
7
8
9
10
11
12

⚠️ 遍历的是副本:

users := []User{{Name: "A"}, {Name: "B"}}
for _, u := range users {
    u.Name = "X" // ❌ 改的是 u 的副本,原 slice 不变
}
1
2
3
4

修改原元素请用索引:users[i].Name = "X"。

# 6.3.5 Go 1.22:for i := range N 整数遍历

// Go 1.22+
for i := range 5 {
    fmt.Println(i) // 0, 1, 2, 3, 4
}

// 等价于
for i := 0; i < 5; i++ { ... }
1
2
3
4
5
6
7

这是个纯语法糖——只为消灭 for i := 0; i < N; i++ 的样板代码。N 必须是非负整数,负数 / 浮点 / nil 都编译错。

# 6.3.6 Go 1.22:循环变量每轮新建

这是 Go 1.22 最影响日常代码的语义改动。看这段代码:

funcs := []func(){}
for _, v := range []int{1, 2, 3} {
    funcs = append(funcs, func() { fmt.Println(v) })
}
for _, f := range funcs { f() }
1
2
3
4
5

Go 1.21 及之前:循环变量 v 是整个 for 共享一个变量,闭包捕获到的是同一个地址,迭代结束 v=3:

3
3
3
1
2
3

Go 1.22 起:每轮迭代 v 是新变量,闭包捕获到不同地址:

1
2
3
1
2
3

这是 Go 1.0 兼容承诺 (opens new window) 的一次罕见突破——官方判断"老语义带来的 bug 远多于偶尔依赖共享变量的代码",所以值得破坏向后兼容。

怎么知道当前模块用的是新语义? 看 go.mod 顶部的 go 1.22 或更高:

module example.com/foo
go 1.22  // ← 1.22+ 启用新循环变量语义
1
2

低于 1.22 的模块继续用老语义。混合使用规则详见 Go FAQ: Loop variable change (opens new window)。

老代码中的兼容写法——v := v 显式 shadow 仍然有效:

for _, v := range items {
    v := v // 老 Go 强制每轮独立,新 Go 多余但无害
    go func() { use(v) }()
}
1
2
3
4

# 6.3.7 Go 1.23:for range func 迭代器

Go 1.23 引入"range over func"——支持自定义迭代器:

// 一个迭代器:按顺序产出 [start, end)
func Range(start, end int) func(yield func(int) bool) {
    return func(yield func(int) bool) {
        for i := start; i < end; i++ {
            if !yield(i) {
                return
            }
        }
    }
}

func main() {
    for i := range Range(10, 13) {
        fmt.Println(i) // 10, 11, 12
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

迭代器函数的签名共三种:

func(yield func() bool)              // Seq0:无值
func(yield func(V) bool)             // Seq[V]:单值
func(yield func(K, V) bool)          // Seq2[K, V]:双值
1
2
3

yield 返回 false 表示"消费方提前 break,迭代器应停止"。标准库 iter、slices.All、maps.Keys 全部基于这套机制。

➡ 卷三第 12 章 迭代器与 range func 详讲。


# 6.4 switch

# 6.4.1 自带 break,不需要写

switch day {
case 1:
    fmt.Println("Monday")
case 2:
    fmt.Println("Tuesday")
default:
    fmt.Println("Other")
}
1
2
3
4
5
6
7
8

与 C 最大的差别:每个 case 默认隐式 break,不会穿透到下一个 case。这是 Go 设计组觉得"C 的默认穿透是错的",直接反过来。

# 6.4.2 fallthrough 显式穿透

如果你确实想穿透,写 fallthrough:

switch x {
case 1:
    fmt.Println("one")
    fallthrough // 直接进入下一个 case 主体(不再判断 case 2 的条件)
case 2:
    fmt.Println("two")
case 3:
    fmt.Println("three")
}
// x=1 输出:one、two
1
2
3
4
5
6
7
8
9
10

注意 fallthrough 是"无条件跳到下一 case 主体"——不重新判断条件。这与某些教材的描述容易混淆。

工程上 fallthrough 极少用。多数想穿透的场景都能用"多值 case"代替。

# 6.4.3 多值 case 与表达式 case

// 多值 case:逗号分隔
switch day {
case 6, 7:
    fmt.Println("Weekend")
case 1, 2, 3, 4, 5:
    fmt.Println("Weekday")
}

// case 可以是任意表达式(不限于常量)
switch {
case x < 0:
    fmt.Println("negative")
case x == 0:
    fmt.Println("zero")
case x > 0:
    fmt.Println("positive")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意第二个写法——省略 switch 后的表达式等价于 switch true,每个 case 是 bool 表达式。这是 Go 替代 if-else-if 长链的"官方"写法。

# 6.4.4 类型 switch(预告,第 10 章详讲)

func describe(i any) {
    switch v := i.(type) {
    case nil:
        fmt.Println("nil")
    case int:
        fmt.Printf("int: %d\n", v)
    case string:
        fmt.Printf("string: %q\n", v)
    case []int:
        fmt.Printf("[]int len=%d\n", len(v))
    default:
        fmt.Printf("unknown type %T\n", v)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

i.(type) 只能用在 switch 的初始化子句里。第 10 章接口与多态会专门讲。

# 6.4.5 无条件 switch 替代 if-else 链

风格对照:

// ❌ 啰嗦
if status == "pending" {
    handlePending()
} else if status == "running" {
    handleRunning()
} else if status == "done" {
    handleDone()
} else {
    handleUnknown()
}

// ✅ Go 风格
switch status {
case "pending":
    handlePending()
case "running":
    handleRunning()
case "done":
    handleDone()
default:
    handleUnknown()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

可读性、可扩展性、性能(编译器可生成跳表)都更好。3 个以上的 if-else-if 一律改 switch。


# 6.5 break / continue 与标签

裸 break / continue 只影响最内层循环。要跳出多层,加标签:

outer:
for i := 0; i < 5; i++ {
    for j := 0; j < 5; j++ {
        if i*j > 6 {
            break outer // 跳出最外层
        }
        if j == i {
            continue outer // 跳到外层下一轮
        }
        fmt.Println(i, j)
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

标签是"goto-but-controlled"——能干 goto 的事,但只能用在 break/continue/goto 上,且只能跳到自己所在或包围的循环 / switch / select 头部。

💡 何时用标签 break?

  • 二维矩阵搜索:找到目标元素立即停搜
  • select 内嵌 for:要从 case 直接跳出整个 for

比起在内层 if found { goto done },标签 break 可读性更好。


# 6.6 goto:什么时候才该用

goto 在 Go 里保留了,但有严格限制:

  • 不能跳过变量声明
  • 不能跳进/跳出函数
  • 不能跨作用域(比如跳进一个 if 块)

实战中用得最多的场景就两个:

场景 1:错误清理(替代 C 的"goto cleanup")

func process() error {
    a, err := openA()
    if err != nil { return err }
    b, err := openB()
    if err != nil { goto closeA }
    c, err := openC()
    if err != nil { goto closeB }
    // ... 业务 ...
    c.Close()
closeB:
    b.Close()
closeA:
    a.Close()
    return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

但 Go 有更优雅的写法——defer。所以这个场景实际不需要 goto:

func process() error {
    a, err := openA()
    if err != nil { return err }
    defer a.Close()
    b, err := openB()
    if err != nil { return err }
    defer b.Close()
    c, err := openC()
    if err != nil { return err }
    defer c.Close()
    // ... 业务 ...
    return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13

场景 2:状态机的状态跳转

写有限状态机时,每个状态用一个标签:

state1:
    if cond1 { goto state2 }
    goto state3
state2:
    ...
    goto state1
state3:
    ...
1
2
3
4
5
6
7
8

但绝大多数状态机用 for { switch state { ... } } 也能写,可读性更高。

结论:Go 工程几乎不写 goto。除非 defer 不能解决的特殊场景(如嵌套循环里的"跳到外层 + 继续清理"),或为了和 C 代码风格保持一致。


# 6.7 没有 while / do-while / 三元

别的语言 Go 等价
while (cond) {} for cond {}
do { ... } while (cond); for { ...; if !cond { break } }
cond ? a : b(三元) if cond { return a }; return b

为什么去掉?同一个理由——少即是多:

  • for 一个关键字覆盖所有循环形态,新人学起来负担更小
  • 没有三元强制写完整 if,避免链式三元 a ? b : c ? d : e 的可读性灾难

如果你真的觉得"三元真有必要",标准库里的写法是:

// max(a, b):Go 1.21+ 内置
m := max(a, b)

// 通用三元:包装成函数
func If[T any](cond bool, a, b T) T {
    if cond { return a }
    return b
}
x := If(score > 60, "pass", "fail")
1
2
3
4
5
6
7
8
9

注意 If 不是短路求值——参数 a, b 都会先求值。性能敏感场景仍然要写完整 if-else。


# 6.8 综合示例:FizzBuzz 与状态机

# 示例 1:FizzBuzz(基础流程语句训练)

// fizzbuzz/main.go
package main

import "fmt"

func main() {
    for i := range 21 { // Go 1.22+
        switch {
        case i == 0:
            continue
        case i%15 == 0:
            fmt.Println("FizzBuzz")
        case i%3 == 0:
            fmt.Println("Fizz")
        case i%5 == 0:
            fmt.Println("Buzz")
        default:
            fmt.Println(i)
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

涉及的本章知识点:

  • for i := range N(Go 1.22+ 整数遍历)
  • 无条件 switch 替代 if-else 链
  • continue 跳过 0
  • 多值 case 略——这里没用到,但可改写为 case i%3 == 0, i%5 == 0: 试试

# 示例 2:红绿灯状态机(标签 + switch)

// trafficlight/main.go
package main

import (
    "fmt"
    "time"
)

type State int

const (
    Red State = iota
    Green
    Yellow
)

func (s State) String() string {
    return [...]string{"Red", "Green", "Yellow"}[s]
}

func (s State) Duration() time.Duration {
    return [...]time.Duration{5, 3, 1}[s] * time.Second
}

func (s State) Next() State {
    return [...]State{Green, Yellow, Red}[s]
}

func main() {
    state := Red
    quit := time.After(20 * time.Second)

loop:
    for {
        select {
        case <-quit:
            fmt.Println("traffic light: shutting down")
            break loop // 跳出 for 而不是只跳出 select
        case <-time.After(state.Duration()):
            fmt.Printf("%s -> %s\n", state, state.Next())
            state = state.Next()
        }
    }
    fmt.Println("done")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

涉及的本章知识点:

知识点 体现位置
标签 loop: 让 break loop 跳出 for,而不只是 select
select 多路等待 quit / 计时器
iota 枚举 Red/Green/Yellow
自定义类型 + String() State.String()
数组转表 [...]State{...}[s] 替代多分支 if

# 6.9 Go 新手陷阱 Top 5

# ❌ 陷阱 1:switch 期望穿透到下一 case

switch x {
case 1:
    a()
case 2: // ❌ x=1 时永远不会执行 b()
    b()
}
1
2
3
4
5
6

修复:要么用 fallthrough,要么改成多值 case 1, 2:。

# ❌ 陷阱 2:break 跳不出嵌套循环

for _, row := range matrix {
    for _, v := range row {
        if v == target {
            break // ❌ 只跳出内层,外层继续
        }
    }
}
1
2
3
4
5
6
7

修复:

search:
for _, row := range matrix {
    for _, v := range row {
        if v == target {
            break search
        }
    }
}
1
2
3
4
5
6
7
8

或用一个 found 标记 + return(更干净)。

# ❌ 陷阱 3:Go 1.21 及之前的循环变量陷阱

// Go 1.21 及之前
var fns []func()
for i := 0; i < 3; i++ {
    fns = append(fns, func() { fmt.Println(i) })
}
for _, f := range fns { f() }
// 输出:3 3 3
1
2
3
4
5
6
7

Go 1.22+ 自动修复。如果你的项目还卡在 go 1.21,升级 go.mod 中的 go 版本到 1.22+ 就能解决。

# ❌ 陷阱 4:range 修改的是副本

type User struct{ Name string }
users := []User{{"A"}, {"B"}}
for _, u := range users {
    u.Name = "X" // ❌ 改副本
}
fmt.Println(users) // [{A} {B}],没变
1
2
3
4
5
6

修复:

for i := range users {
    users[i].Name = "X" // ✅ 通过索引改原元素
}
1
2
3

# ❌ 陷阱 5:for-range map 期望顺序稳定

for k := range m {
    fmt.Println(k) // 顺序每次都不一样
}
1
2
3

修复:先排序 key,再按顺序遍历——见 5.4.4。


# 6.10 思考题

  1. 为什么 Go 把 for 设计成"一个关键字打天下"?这种统一相对 C 的 for / while / do-while 三件套,在学习成本与表达力上的取舍?
  2. Go 1.22 循环变量改动违反"Go 1 兼容承诺",但仍被推行——请查阅官方文档,列举支持这个决定的两个核心论据。
  3. 写一段代码:用带标签的 continue 实现"跳过 i==j 时整个外层循环"。
  4. switch x.(type) 与 switch v := x.(type) 的区别?什么时候必须用后者?
  5. 用 Go 实现一个 do-while:先执行一次再判断条件。提示:for 的某种形态。
  6. 为什么 goto 不能跳过变量声明?请用一个能编译失败的例子说明。
  7. Go 没有三元运算符。如果团队代码 base 大量 if cond { x = a } else { x = b },是否值得引入泛型 If[T] 函数?请从可读性、性能、调试三个角度分析。

# 6.11 推荐阅读

# 卷内

  • 第 5 章 复合类型(map 遍历的随机性)
  • 第 7 章 函数(defer 替代 goto cleanup)
  • 第 12 章 并发 goroutine(select 详讲)
  • 第 13 章 通道 channel(for range ch)

# 跨卷

  • 卷三第 12 章 迭代器与 range func(Go 1.23 新特性)
  • 卷四第 6 章 常见反模式(goto 与"提前 return"哪个更好)

# 外部资料

  • Go 官方规范:For statements (opens new window)
  • Go 官方规范:Switch statements (opens new window)
  • Fixing for loops in Go 1.22 (opens new window) — 循环变量改动设计文档
  • Range over function types (opens new window) — Go 1.23 迭代器
上次更新: 2026/06/10, 11:13:41
复合类型
函数

← 复合类型 函数→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式