循环控制 #

一、break语句 #

1.1 基本用法 #

break用于跳出当前循环:

go
for i := 0; i < 10; i++ {
    if i == 5 {
        break
    }
    fmt.Println(i)
}
// 输出: 0 1 2 3 4

1.2 在switch中使用 #

go
n := 2
switch n {
case 1:
    fmt.Println("一")
case 2:
    fmt.Println("二")
    break  // 可选,Go的switch默认break
case 3:
    fmt.Println("三")
}

1.3 跳出嵌套循环 #

使用标签跳出外层循环:

go
outer:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if i == 1 && j == 1 {
            break outer
        }
        fmt.Printf("(%d, %d) ", i, j)
    }
}
// 输出: (0, 0) (0, 1) (0, 2) (1, 0)

1.4 实际应用 #

查找元素:

go
func find(matrix [][]int, target int) (int, int) {
    for i, row := range matrix {
        for j, val := range row {
            if val == target {
                return i, j
            }
        }
    }
    return -1, -1
}

提前退出:

go
func processUntilError(items []string) error {
    for _, item := range items {
        if err := process(item); err != nil {
            return err
        }
    }
    return nil
}

二、continue语句 #

2.1 基本用法 #

continue跳过本次迭代,继续下一次:

go
for i := 0; i < 5; i++ {
    if i == 2 {
        continue
    }
    fmt.Println(i)
}
// 输出: 0 1 3 4

2.2 在for-range中使用 #

go
numbers := []int{1, 2, 3, 4, 5}

for _, n := range numbers {
    if n%2 == 0 {
        continue
    }
    fmt.Println(n)
}
// 输出: 1 3 5

2.3 配合标签使用 #

跳到外层循环的下一次迭代:

go
outer:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if j == 1 {
            continue outer
        }
        fmt.Printf("(%d, %d) ", i, j)
    }
}
// 输出: (0, 0) (1, 0) (2, 0)

2.4 实际应用 #

过滤处理:

go
func processValid(items []string) {
    for _, item := range items {
        if !isValid(item) {
            continue
        }
        process(item)
    }
}

跳过空值:

go
for _, line := range lines {
    line = strings.TrimSpace(line)
    if line == "" || strings.HasPrefix(line, "#") {
        continue
    }
    processLine(line)
}

三、goto语句 #

3.1 基本用法 #

goto无条件跳转到标签:

go
i := 0
start:
fmt.Println(i)
i++
if i < 5 {
    goto start
}
// 输出: 0 1 2 3 4

3.2 标签定义 #

标签定义在语句前:

go
label:
    // 语句

3.3 实际应用 #

错误处理:

go
func process() error {
    var err error
    
    if err = step1(); err != nil {
        goto cleanup
    }
    
    if err = step2(); err != nil {
        goto cleanup
    }
    
    if err = step3(); err != nil {
        goto cleanup
    }
    
    return nil
    
cleanup:
    doCleanup()
    return err
}

资源释放:

go
func readFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        goto error
    }
    
    data, err := io.ReadAll(f)
    if err != nil {
        goto closeFile
    }
    
    f.Close()
    return data, nil
    
closeFile:
    f.Close()
error:
    return nil, err
}

3.4 goto注意事项 #

  • 不能跳过变量声明:
go
goto label
x := 10  // 错误:跳过变量声明
label:
fmt.Println(x)
  • 不能跳入代码块:
go
goto label  // 错误:不能跳入for块
for i := 0; i < 5; i++ {
label:
    fmt.Println(i)
}

四、标签 #

4.1 定义规则 #

  • 标签名在当前函数内唯一
  • 标签后必须有语句
  • 可以定义在for、switch、select之前
go
outer:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if i == j {
            continue outer
        }
        fmt.Printf("(%d, %d) ", i, j)
    }
}

4.2 标签作用域 #

标签只在当前函数内有效:

go
func f1() {
label:
    for i := 0; i < 3; i++ {
        break label
    }
}

func f2() {
    // break label  // 错误:label不在当前函数
}

五、实际应用示例 #

5.1 查找二维数组 #

go
func findInMatrix(matrix [][]int, target int) (int, int, bool) {
    for i, row := range matrix {
        for j, val := range row {
            if val == target {
                return i, j, true
            }
        }
    }
    return -1, -1, false
}

5.2 解析配置文件 #

go
func parseConfig(lines []string) (*Config, error) {
    config := &Config{}
    
    for _, line := range lines {
        line = strings.TrimSpace(line)
        
        if line == "" || strings.HasPrefix(line, "#") {
            continue
        }
        
        parts := strings.SplitN(line, "=", 2)
        if len(parts) != 2 {
            continue
        }
        
        key := strings.TrimSpace(parts[0])
        value := strings.TrimSpace(parts[1])
        
        switch key {
        case "host":
            config.Host = value
        case "port":
            port, err := strconv.Atoi(value)
            if err != nil {
                return nil, err
            }
            config.Port = port
        }
    }
    
    return config, nil
}

5.3 重试逻辑 #

go
func retryWithBackoff(fn func() error, maxAttempts int) error {
    var lastErr error
    backoff := time.Second
    
    for attempt := 0; attempt < maxAttempts; attempt++ {
        if err := fn(); err == nil {
            return nil
        } else {
            lastErr = err
        }
        
        time.Sleep(backoff)
        backoff *= 2
        if backoff > 30*time.Second {
            backoff = 30 * time.Second
        }
    }
    
    return fmt.Errorf("after %d attempts: %v", maxAttempts, lastErr)
}

六、最佳实践 #

6.1 优先使用break/continue #

go
// 推荐
for {
    if done {
        break
    }
}

// 不推荐
loop:
for {
    if done {
        goto end
    }
}
end:

6.2 避免过度使用goto #

go
// 不推荐:使用goto模拟循环
i := 0
start:
fmt.Println(i)
i++
if i < 5 {
    goto start
}

// 推荐:使用for
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

6.3 使用有意义的标签名 #

go
// 好
outerLoop:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        break outerLoop
    }
}

// 不好
x:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        break x
    }
}

七、总结 #

循环控制要点:

语句 说明
break 跳出当前循环
continue 跳过本次迭代
goto 无条件跳转

关键点:

  1. break:跳出循环或switch
  2. continue:跳过本次迭代
  3. goto:谨慎使用,避免造成混乱
  4. 标签:配合break/continue跳出嵌套循环
  5. 作用域:标签只在当前函数内有效

准备好学习函数了吗?让我们进入下一章!

最后更新:2026-03-26