Files
OpenFlare/.agents/skills/go-control-flow/SKILL.md
T
2026-08-04 11:40:32 +08:00

5.4 KiB
Raw Blame History

name, description, license, metadata
name description license metadata
go-control-flow Use when writing conditionals, loops, or switch statements in Go — including if with initialization, early returns, for loop forms, range, switch, type switches, and blank identifier patterns. Also use when writing a simple if/else or for loop, even if the user doesn't mention guard clauses or variable scoping. Does not cover error flow patterns (see go-error-handling). Apache-2.0
sources
Effective Go, Google Style Guide

Go 控制流

在使用 switch 语句、类型 switch 或带标签的 break 时,阅读 references/SWITCH-PATTERNS.md

在使用 _、空白标识符导入或编译时接口检查时,阅读 references/BLANK-IDENTIFIER.md


带初始化的 If

if 和 switch 接受可选的初始化语句。使用它将变量限定在条件块作用域内:

if err := file.Chmod(0664); err != nil {
    log.Print(err)
    return err
}

如果需要在 if 之后超出几行的范围使用该变量,请单独声明并使用标准 if:

x, err := f()
if err != nil {
    return err
}
// 大量使用 x 的代码

缩进错误流(守卫子句)

当 if 主体以 break、continue、goto 或 return 结尾时,省略不必要的 else。保持成功路径不缩进:

f, err := os.Open(name)
if err != nil {
    return err
}
d, err := f.Stat()
if err != nil {
    f.Close()
    return err
}
codeUsing(f, d)

当 if 已经返回时,绝不要将正常流程埋在 else 中。


重新声明和重新赋值

:= 短声明允许在同一作用域中重新声明变量:

f, err := os.Open(name)  // 声明 f 和 err
d, err := f.Stat()       // 声明 d,重新赋值 err

变量 v 即使已经声明过,也可以出现在 := 声明中,前提是:

  1. 声明在与现有 v 相同的作用域中
  2. 值可赋值给 v
  3. 声明中至少创建了一个其他新变量

变量遮蔽

警告:如果 v 在外层作用域中声明,:= 会创建一个新的遮蔽变量 — 这是常见的 bug 来源:

// Bug:if 块内的 ctx 遮蔽了外层的 ctx
if *shortenDeadlines {
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()
}
// 此处的 ctx 仍然是原始的 — 被遮蔽的 ctx 没有逃逸

// 修复:使用 = 而不是 :=
var cancel func()
ctx, cancel = context.WithTimeout(ctx, 3*time.Second)

For 循环

Go 的 for 是唯一的循环结构,统一了 while、do-while 和 C 风格的 for:

// 仅条件(Go 的 "while")
for x > 0 {
    x = process(x)
}

// 无限循环
for {
    if done() { break }
}

// C 风格的三组件形式
for i := 0; i < n; i++ { ... }

Range

range 遍历切片、映射、字符串和通道:

for i, v := range slice { ... }   // 索引 + 值
for k, v := range myMap { ... }   // 键 + 值(顺序不确定)
for i, r := range "héllo" { ... } // 字节索引 + rune(不是字节)
for v := range ch { ... }         // 接收直到通道关闭

关键规则:

  • 对字符串 range 产生 rune,不是字节 — i 是字节偏移量
  • 对映射 range 的顺序不确定 — 不要依赖它
  • 使用 _ 丢弃索引或值:for _, v := range slice

并行赋值

Go 没有逗号运算符。使用并行赋值处理多个循环变量:

for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
    a[i], a[j] = a[j], a[i]
}

++ 和 -- 是语句,不是表达式 — 它们不能出现在并行赋值中。


Switch:带标签的 Break

for 循环内 switch 中的 break 只会中断 switch。使用带标签的 break 退出外层循环:

Loop:
    for _, v := range items {
        switch v.Type {
        case "done":
            break Loop  // 中断 for 循环
        }
    }

关于类型 switch,参见 go-interfaces:类型 Switch。


空白标识符

绝不要随意丢弃错误 — 空指针解引用 panic 可能随之而来。

在编译时验证接口实现:var _ io.Writer = (*MyType)(nil)。 参见 go-interfaces 中的接口满足检查模式。


快速参考

模式 Go 惯用法
If 初始化 if err := f(); err != nil { }
提前返回 当 if 主体返回时省略 else
重新声明 := 在相同作用域 + 新变量时重新赋值
遮蔽陷阱 := 在内层作用域创建新变量
并行赋值 i, j = i+1, j-1
无表达式 switch switch { case cond: }
逗号 case case 'a', 'b', 'c':
无 fallthrough 默认行为(需要时显式使用 fallthrough)
从 switch 中跳出循环 break Label
丢弃值 _, err := f()
副作用导入 import _ "pkg"
接口检查 var _ Interface = (*Type)(nil)

相关技能

  • 错误流程:在构建守卫子句、提前返回或错误优先模式时,参见 go-error-handling
  • 类型 switch:在使用类型 switch、comma-ok 惯用法或接口满足检查时,参见 go-interfaces
  • 减少嵌套:在减少嵌套深度或解决格式问题时,参见 go-style-core
  • 变量作用域:在使用 if 初始化、:= 重新声明或减少变量作用域时,参见 go-declarations