Go break、continue、goto 详解

Go 编程实战系列(二十)

上一篇《Go for 循环详解》中,我们学习了 Go 中唯一的循环关键字 for,包括普通循环、无限循环、range 遍历以及嵌套循环等内容。本篇继续学习流程控制中的三个重要关键字:

kotlin 复制代码
break
continue
goto

它们都可以改变程序原本的执行流程,但作用完全不同。简单来说:

arduino 复制代码
break     结束循环或 switch
continue  跳过当前循环,继续下一次
goto      直接跳转到指定标签

在实际 Go 项目中,breakcontinue 使用非常普遍,而 goto 使用频率相对较低。掌握三者的区别,可以帮助我们写出更加清晰的流程控制代码。

一、break 是什么?

break 的作用可以理解为:

立即结束当前循环或 switch。

例如:

css 复制代码
package main

import "fmt"

func main() {
	for i := 1; i <= 10; i++ {
		if i == 5 {
			break
		}

		fmt.Println(i)
	}
}

输出:

复制代码
1
2
3
4

当:

ini 复制代码
i == 5

条件成立后执行:

kotlin 复制代码
break

整个 for 循环立即结束,因此:

复制代码
5
6
7
8
9
10

都不会继续处理。

二、break 与普通循环结束的区别

普通循环:

css 复制代码
for i := 1; i <= 5; i++ {
	fmt.Println(i)
}

是因为:

css 复制代码
i <= 5

最终变成 false,所以循环自然结束。

而:

kotlin 复制代码
break

是不管循环条件是否仍然成立,都直接结束循环。

例如:

css 复制代码
for i := 1; i <= 10000; i++ {
	if i == 10 {
		break
	}

	fmt.Println(i)
}

虽然原本可以循环到 10000,但是运行到 10 时就直接退出。

三、break 查找数据

实际开发中,break 很适合用于查找数据。

例如:

go 复制代码
users := []string{
	"Tom",
	"Jack",
	"Lucy",
	"Bob",
}

for _, user := range users {
	if user == "Lucy" {
		fmt.Println("找到用户:", user)
		break
	}
}

输出:

复制代码
找到用户: Lucy

找到 Lucy 后,就没有必要继续检查 Bob,因此使用 break 提前结束循环。

如果有 10 万条数据,而目标在第 100 条就已经找到,那么继续遍历剩余数据通常没有意义。

四、break 结束无限循环

上一篇学习过:

复制代码
for {
}

表示无限循环。

无限循环经常配合 break 使用:

markdown 复制代码
count := 1

for {
	fmt.Println(count)

	if count >= 5 {
		break
	}

	count++
}

输出:

复制代码
1
2
3
4
5

这种:

kotlin 复制代码
for {
	if 条件 {
		break
	}
}

在命令行工具、任务处理等场景中非常常见。

五、break 与 switch

break 不仅可以结束 for,也可以结束 switch

不过上一篇已经学习过,Go 的 switch 默认执行完一个 case 后就会自动退出,因此普通情况下不需要手动写:

kotlin 复制代码
break

例如:

css 复制代码
status := 1

switch status {
case 1:
	fmt.Println("待付款")
case 2:
	fmt.Println("已付款")
}

case 1 执行完成后,不会自动进入 case 2

所以没有必要:

arduino 复制代码
case 1:
	fmt.Println("待付款")
	break

但是在 case 内部需要提前退出 switch 时,可以主动使用 break

六、continue 是什么?

continuebreak 很容易混淆。

break

复制代码
整个循环结束

continue

复制代码
只结束当前这一轮
然后继续下一轮循环

例如:

css 复制代码
for i := 1; i <= 5; i++ {
	if i == 3 {
		continue
	}

	fmt.Println(i)
}

输出:

复制代码
1
2
4
5

当:

ini 复制代码
i == 3

执行 continue 后:

css 复制代码
fmt.Println(i)

不会执行。

但是整个循环并没有结束,而是继续处理:

复制代码
4
5

七、break 与 continue 的区别

来看两个例子。

使用 break:

css 复制代码
for i := 1; i <= 5; i++ {
	if i == 3 {
		break
	}

	fmt.Println(i)
}

输出:

复制代码
1
2

使用 continue:

css 复制代码
for i := 1; i <= 5; i++ {
	if i == 3 {
		continue
	}

	fmt.Println(i)
}

输出:

复制代码
1
2
4
5

因此可以简单记住:

kotlin 复制代码
break
↓
后面的循环全部不要了

continue
↓
当前这一次不要了
后面的循环继续

八、continue 过滤数据

continue 在实际项目中非常适合过滤不需要处理的数据。

例如:

go 复制代码
scores := []int{
	90,
	55,
	80,
	40,
	100,
}

for _, score := range scores {
	if score < 60 {
		continue
	}

	fmt.Println("及格成绩:", score)
}

输出:

复制代码
及格成绩: 90
及格成绩: 80
及格成绩: 100

遇到不及格的数据:

复制代码
score < 60

直接跳过。

这种写法通常比:

arduino 复制代码
if score >= 60 {
	// 大量业务代码
}

更容易减少代码嵌套。

九、continue 处理无效数据

例如我们有一批用户:

go 复制代码
users := []string{
	"Tom",
	"",
	"Jack",
	"",
	"Lucy",
}

空字符串表示无效用户。

可以:

sql 复制代码
for _, user := range users {
	if user == "" {
		continue
	}

	fmt.Println("处理用户:", user)
}

输出:

复制代码
处理用户: Tom
处理用户: Jack
处理用户: Lucy

实际项目中经常使用类似结构:

go 复制代码
for _, item := range items {
	if item 无效 {
		continue
	}

	处理 item
}

也就是先排除不需要的数据,再处理正常数据。

十、continue 使用时需要注意什么?

下面这种 while 风格循环要特别小心:

css 复制代码
i := 0

for i < 10 {
	if i == 5 {
		continue
	}

	i++
}

当:

ini 复制代码
i == 5

执行 continue 后:

css 复制代码
i++

不会执行。

于是 i 永远等于 5,程序形成死循环。

应该保证循环变量能够正常更新,例如:

css 复制代码
i := 0

for i < 10 {
	i++

	if i == 5 {
		continue
	}

	fmt.Println(i)
}

所以使用 continue 时,一定要注意是否跳过了关键的状态更新代码。

十一、嵌套循环中的 break

例如:

css 复制代码
for i := 1; i <= 3; i++ {
	for j := 1; j <= 3; j++ {
		if j == 2 {
			break
		}

		fmt.Println(i, j)
	}
}

输出:

复制代码
1 1
2 1
3 1

这里需要注意:

kotlin 复制代码
break

默认只结束:

当前所在的最内层循环。

也就是说,它只结束:

css 复制代码
for j := 1; j <= 3; j++ {
}

外层:

css 复制代码
for i := 1; i <= 3; i++ {
}

仍然会继续执行。

十二、如何一次退出多层循环?

假设:

css 复制代码
for i := 1; i <= 10; i++ {
	for j := 1; j <= 10; j++ {
		if i == 3 && j == 3 {
			// 希望整个循环全部结束
		}
	}
}

普通 break 只能退出内层循环。

Go 可以使用:

css 复制代码
Label

也就是标签。

例如:

css 复制代码
Outer:
	for i := 1; i <= 10; i++ {
		for j := 1; j <= 10; j++ {
			if i == 3 && j == 3 {
				break Outer
			}

			fmt.Println(i, j)
		}
	}

这里:

makefile 复制代码
Outer:

定义一个标签。

然后:

kotlin 复制代码
break Outer

直接结束标签对应的循环。

这种方式在复杂嵌套循环中比较有用。

十三、带标签的 continue

continue 也可以配合标签。

例如:

css 复制代码
Outer:
	for i := 1; i <= 3; i++ {
		for j := 1; j <= 3; j++ {
			if j == 2 {
				continue Outer
			}

			fmt.Println(i, j)
		}
	}

当:

ini 复制代码
j == 2

执行:

kotlin 复制代码
continue Outer

会直接进入外层循环的下一次迭代。

需要注意,带标签的 continue 比普通 continue 更难阅读,因此没有明确需求时不要滥用。

十四、goto 是什么?

除了:

kotlin 复制代码
break
continue

Go 还保留了一个比较特殊的流程控制关键字:

arduino 复制代码
goto

goto 的作用是:

无条件跳转到当前函数中的指定标签。

基本语法:

arduino 复制代码
goto 标签名称

标签名称:
	代码

例如:

go 复制代码
package main

import "fmt"

func main() {
	fmt.Println("第一步")

	goto End

	fmt.Println("第二步")

End:
	fmt.Println("程序结束")
}

输出:

复制代码
第一步
程序结束

这行:

arduino 复制代码
fmt.Println("第二步")

没有执行。

因为:

vbnet 复制代码
goto End

直接跳到了:

makefile 复制代码
End:

的位置。

十五、goto 标签

标签的基本形式:

makefile 复制代码
标签名称:

例如:

makefile 复制代码
Start:

或者:

makefile 复制代码
End:

然后通过:

arduino 复制代码
goto Start

进行跳转。

例如:

dart 复制代码
num := 1

Start:
	fmt.Println(num)
	num++

	if num <= 5 {
		goto Start
	}

输出:

复制代码
1
2
3
4
5

从功能上看,这相当于实现了一个循环。

但是这种写法并不推荐。

更清晰的方式应该是:

dart 复制代码
for num := 1; num <= 5; num++ {
	fmt.Println(num)
}

十六、为什么不推荐大量使用 goto?

goto 最大的问题不是不能用,而是:

使用过多会让程序执行流程变得难以理解。

例如:

less 复制代码
goto A

B:
	// 一些代码
	goto C

A:
	// 一些代码
	goto B

C:
	// 一些代码

代码执行顺序不再是简单的从上到下。

阅读代码时需要不断寻找:

css 复制代码
A 在哪里?
B 在哪里?
C 又在哪里?

如果项目越来越大,这种代码维护成本会非常高。

因此普通业务逻辑优先使用:

kotlin 复制代码
if
switch
for
break
continue
return

而不是大量使用 goto

十七、goto 不能随意跳过变量声明

Go 对 goto 有一定限制。

例如不能通过 goto 跳过变量声明,然后跳到该变量已经进入作用域的位置。

类似下面这种代码会产生编译错误:

vbnet 复制代码
goto End

name := "Tom"

End:
	fmt.Println(name)

因为跳转直接绕过了:

go 复制代码
name := "Tom"

Go 会限制这种可能导致变量状态不明确的跳转。

因此 goto 并不是可以在函数中毫无限制地随意跳转。

十八、goto 的适用场景

虽然日常业务开发不建议大量使用 goto,但并不代表它完全没有价值。

例如复杂的多层资源处理:

复制代码
步骤1
↓
步骤2
↓
步骤3
↓
发生错误
↓
统一清理

某些底层代码可能使用 goto 跳转到统一处理位置。

例如概念上:

vbnet 复制代码
if err1 != nil {
	goto Error
}

if err2 != nil {
	goto Error
}

if err3 != nil {
	goto Error
}

return

Error:
	fmt.Println("处理错误")

不过在现代 Go 代码中,更多时候会使用:

go 复制代码
defer

完成资源清理。

例如:

go 复制代码
file, err := os.Open("data.txt")

if err != nil {
	return
}

defer file.Close()

因此以后学习 defer 后,会发现大多数资源释放问题并不需要 goto。

十九、break、continue、return、goto 的区别

这几个关键字很容易混淆,可以简单总结:

kotlin 复制代码
break
结束循环或 switch

continue
跳过当前循环,继续下一轮

return
结束当前函数

goto
跳转到当前函数中的指定标签

例如:

go 复制代码
for _, user := range users {
	if user == "" {
		continue
	}

	if user == "admin" {
		break
	}
}

这里:

kotlin 复制代码
continue

遇到空用户只是跳过。

而:

kotlin 复制代码
break

遇到 admin 后直接结束整个循环。

如果改成:

kotlin 复制代码
return

那么结束的不只是循环,而是当前整个函数。

二十、实际案例:查找第一个符合条件的数据

例如:

go 复制代码
numbers := []int{
	3,
	7,
	11,
	20,
	30,
}

for _, num := range numbers {
	if num%2 == 0 {
		fmt.Println("找到第一个偶数:", num)
		break
	}
}

输出:

复制代码
找到第一个偶数: 20

找到目标后立即结束循环,可以避免不必要的遍历。

二十一、实际案例:过滤无效订单

例如:

go 复制代码
orders := []int{
	100,
	0,
	200,
	-1,
	300,
}

total := 0

for _, price := range orders {
	if price <= 0 {
		continue
	}

	total += price
}

fmt.Println("有效订单总金额:", total)

输出:

复制代码
有效订单总金额: 600

这里:

kotlin 复制代码
if price <= 0 {
	continue
}

负责过滤无效数据。

剩下的正常数据才执行:

makefile 复制代码
total += price

这是一种非常常见的 Go 代码结构。

二十二、实际案例:用户数据处理

例如:

go 复制代码
package main

import "fmt"

func main() {
	users := []string{
		"Tom",
		"",
		"Jack",
		"admin",
		"Lucy",
	}

	for _, user := range users {
		if user == "" {
			continue
		}

		if user == "admin" {
			fmt.Println("发现管理员,停止处理")
			break
		}

		fmt.Println("处理用户:", user)
	}
}

输出:

复制代码
处理用户: Tom
处理用户: Jack
发现管理员,停止处理

这里同时使用了:

kotlin 复制代码
continue
break

逻辑非常清楚:

复制代码
无效数据
↓
跳过

普通用户
↓
正常处理

发现管理员
↓
结束循环

二十三、什么时候使用 break?

适合以下情况:

复制代码
已经找到目标数据
已经满足退出条件
用户主动选择退出
发生某种情况后无需继续遍历
需要结束无限循环

例如:

kotlin 复制代码
if found {
	break
}

二十四、什么时候使用 continue?

适合:

复制代码
当前数据无效
当前数据不需要处理
过滤特定数据
跳过错误数据
只处理符合条件的数据

例如:

go 复制代码
for _, user := range users {
	if !user.Enabled {
		continue
	}

	process(user)
}

这种代码在实际项目中非常常见。

二十五、什么时候使用 goto?

goto 应该更加谨慎。

普通业务逻辑中,如果可以使用:

go 复制代码
if
for
switch
return
defer

清晰解决,就没有必要使用 goto

如果确实存在复杂的多层流程跳转,而且使用 goto 能明显让代码更加简单,可以考虑。

判断标准不是:

goto 能不能用?

而应该是:

使用 goto 后,代码是不是更容易理解和维护?

如果答案是否定的,就不要使用。

二十六、常见错误

第一种错误是把 breakcontinue 混淆。

例如:

go 复制代码
for _, user := range users {
	if user == "" {
		break
	}
}

如果只是想跳过空数据,这里应该使用:

kotlin 复制代码
continue

否则遇到第一个空数据后,后面的所有用户都不会处理。

第二种错误是在 while 风格循环中因为 continue 导致循环变量无法更新:

css 复制代码
i := 0

for i < 10 {
	if i == 5 {
		continue
	}

	i++
}

会形成死循环。

第三种错误是大量使用 goto:

arduino 复制代码
goto A
goto B
goto C

虽然某些情况下可以运行,但会明显降低代码可读性。

第四种错误是在嵌套循环中误以为普通 break 会结束所有循环。实际上:

kotlin 复制代码
break

默认只退出最内层循环。

如果需要退出指定外层循环,可以考虑标签:

kotlin 复制代码
Outer:
	for {
		for {
			break Outer
		}
	}

二十七、本章小结

本篇介绍了 Go 中三个重要的流程控制关键字:

kotlin 复制代码
break
continue
goto

其中 break 用于:

arduino 复制代码
结束 for
结束 switch
提前停止数据查找
退出无限循环

continue 用于:

复制代码
跳过当前循环
继续下一轮
过滤无效数据
减少代码嵌套

goto 用于:

复制代码
跳转到指定标签

还学习了:

css 复制代码
Label 标签
break Label
continue Label
goto Label

需要重点记住:

  1. break 是结束整个当前循环;
  2. continue 是跳过当前这一轮;
  3. 嵌套循环中的普通 break 只结束最内层循环;
  4. 标签可以控制指定的外层循环;
  5. goto 可以进行无条件跳转;
  6. 普通业务开发不要滥用 goto
  7. Go 中通常优先使用 ifforreturndefer 等结构保持代码清晰。

到这里,Go 基础流程控制部分已经基本学习完成。接下来我们开始进入非常重要的数据结构部分。

本章练习

  1. 使用 for 输出 1~100,在数字等于 50 时使用 break 结束循环。
  2. 输出 1~20,使用 continue 跳过所有偶数。
  3. 遍历用户列表,遇到空用户名时跳过。
  4. 遍历商品列表,找到目标商品后使用 break
  5. 编写一个无限循环,在计数达到 10 时退出。
  6. 编写两层循环,观察普通 break 会结束哪一层。
  7. 使用 break Label 一次退出两层循环。
  8. 使用 continue Label 观察外层循环的执行过程。
  9. 编写一个简单 goto 示例,并尝试使用普通 for 重写。

下一篇预告

《Go 数组 Array 详解》

下一篇将正式进入 Go 数据结构的学习,首先介绍最基础的数组 Array,包括:

  • 什么是数组;

  • 数组的声明;

  • 数组初始化;

  • 自动推导数组长度;

  • 根据索引访问元素;

  • 修改数组元素;

  • 数组长度 len

  • for 遍历数组;

  • for range 遍历数组;

  • 多维数组;

  • 数组比较;

  • 数组作为函数参数;

  • Go 数组是值类型;

  • 数组与切片的区别;

  • 数组越界问题;

  • 实际开发案例。

理解数组之后,下一步就可以学习 Go 中使用频率更高、也更加重要的数据结构------Slice 切片。

相关推荐
凤山老林2 小时前
复杂检索引擎落地:Spring Boot + Elasticsearch 数据同步与高阶查询实战
spring boot·后端·elasticsearch
ShineWinsu9 小时前
对于C++:IO流状态的详细解析
c++·面试·io·cin·io流状态·goodbit·failbit
「PlanA」9 小时前
测试开发面经001
面试
笃行3509 小时前
SQLServer数据迁移之后,那张报表还能不能秒出
后端
LucianaiB10 小时前
别再被 AI 骗了:我把腾讯云 4 个 Skill 做成了个「AI 打假侦探」,过程的一个小配置坑惨了我
后端
JL1510 小时前
面试项目被问到自闭-如何把课设包装成亮眼项目
面试·职场和发展
candyTong10 小时前
Claude Code 如何恢复一段会话
后端·架构·ai编程
En^_^Joy11 小时前
Django项目配置全攻略:settings配置文件
后端·python·django
IT_陈寒14 小时前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端