英文:
golang's fallthrough seems unexpected
问题
我有以下代码:
package main
import (
"fmt"
)
func main() {
switch {
case 1 == 1:
fmt.Println("1 == 1")
fallthrough
case 2 == 1:
fmt.Println("2 == 1")
}
}
这段代码在Go Playground上打印了两行结果- 在这里查看示例。我本以为fallthrough
语句会包括对下一个case
语句的评估,但事实似乎并非如此。
当然,我可以始终使用一堆if
语句,所以这并不是一个真正的障碍,但我很好奇这里的意图是什么,因为这对我来说是一个不明显的结果。
有人可以解释一下吗?例如:在这个代码中,我如何使第一个和第三个case
执行?
英文:
I have the following code:
package main
import (
"fmt"
)
func main() {
switch {
case 1 == 1:
fmt.Println("1 == 1")
fallthrough
case 2 == 1:
fmt.Println("2 == 1")
}
}
Which prints both lines on the go playground - see example here. I would have expected the fallthrough statement to include evaluation of the next case
statement, but this seems not to be the case.
Of course, I can always use a bunch of if
statements, so this is not a real impediment, but I am curious what the intention here is, since this seems to me to be a non-obvious result.
Anyone care to explain? For example: in this code, how can I get the 1st and 3rd cases to execute?
答案1
得分: 6
Switch语句不是一堆if语句。它更类似于if {} else if {}
结构,但有一些区别,即break
和fallthrough
。Switch语句不会执行第一个和第三个条件 - 它不会检查每个条件,而是找到第一个匹配并执行它。
它的主要目的是遍历可能的值列表,并为每个值执行不同的代码。实际上,在C语言中(switch语句来自于C语言),switch表达式只能是整数类型,case值只能是与switch表达式进行比较的常量。直到相对最近,一些语言才开始支持在switch语句中使用字符串、布尔表达式等。
至于fallthrough逻辑,它也来自于C语言。在C语言中没有fallthrough运算符。在C语言中,执行会顺序进入下一个case(不检查case值),除非遇到break运算符。这种设计的原因是有时你需要做一些特殊的操作,然后执行与另一个case相同的步骤。因此,这种设计允许这样做。不幸的是,这种情况相对较少见,因此默认情况下的fallthrough会导致更多的麻烦,当程序员忘记放置break语句时,而不是在真正有意省略break时提供帮助。因此,许多现代语言将这种逻辑更改为默认情况下不允许fallthrough,并要求显式的fallthrough语句,如果确实需要fallthrough。
不幸的是,很难找到一个非人为的fallthrough有用的例子,而且这个例子足够简短,适合放在一个答案中。正如我所说,这种情况相对较少见。但有时你需要编写类似于以下代码的代码:
if x == a || x == b {
if x == a {
// do action a
}
// do action ab
} else if x == c {
// do action c
} else if x == d {
// do action d
}
实际上,最近我在我的一个项目中需要类似结构的代码。所以,我使用了switch语句。代码如下:
switch x {
case a: // do action a
fallthrough
case b: // do action ab
case c: // do action c
case d: // do action d
}
你提问中的switch语句在功能上等同于以下代码:
if 1 == 1 || 2 == 1 {
if 1 == 1 {
fmt.Println("1 == 1")
}
fmt.Println("2 == 1")
}
英文:
Switch is not a bunch of ifs. It's more akin to if {} else if {}
construct, but with a couple of twists - namely break
and fallthrough
. It's not possible to make switch execute first and third cases - a switch does not check each condition, it finds first match and executes it. That's all.
It's primary purpose is to walk through a list of possible values and execute a different code for each value. In fact, in C (where switch statement came from) switch expression can only be of integral type and case values can only be constants that switch expression will be compared too. It's only relatively recently, languages started adding support for strings, boolean expressions etc in switch cases.
As to fallthrough logic it also comes from C. There is no fallthrough operator in C. In C execution falls through into next case (without checking case values) unless break operator encountered. The reason for this design is that sometimes you need to do something special and then do same steps as in another case. So, this design merely allows that. Unfortunately, it's rather rarely useful, so falling through by default was causing more trouble when programmer forgotten to put a break statement in, then actually helping when truly omitted that break intentionally. So, many modern languages change this logic to never fall through by default and to require explicit fallthrough statement if falling through is actually required.
Unfortunately, it's a it hard to come up with a non contrived example of fallthrough being useful that would be short enough to fit into an answer. As I said it's relatively rare. But sometimes you need to write code similar to this:
if x == a || x == b {
if x == a {
// do action a
}
// do action ab
} else if x == c {
// do action c
} else if x == d {
// do action d
}
In fact, I needed code of similar structure quite recently in one of my projects. So, I used switch statement instead. And it looked like this:
switch x {
case a: // do action a
fallthrough
case b: // do action ab
case c: // do action c
case d: // do action d
}
And your switch from the question is functionally equivalent to this:
if 1 == 1 || 2 == 1 {
if 1 == 1 {
fmt.Println("1 == 1")
}
fmt.Println("2 == 1")
}
答案2
得分: 5
可以理解为,Go语言中的fallthrough
行为是模仿C语言的,而C语言一直以来都是这样工作的。在C语言中,switch
语句只是条件跳转链的简写形式,所以你的例子会被编译成以下形式:
// 伪代码
if 1 == 1 goto alpha
if 2 == 1 goto beta
alpha:
fmt.Println("1 == 1")
beta:
fmt.Println("2 == 1")
可以看到,一旦执行进入alpha
分支,它会继续向下执行,忽略beta
标签(因为标签本身并不起作用)。条件检查已经完成,不会再次进行。
因此,fallthrough
语句在switch
语句中的非直观性,只是因为switch
语句本质上就是伪装的跳转语句。
英文:
Presumably, Go's fallthrough behavior is modeled after C, which always worked like this. In C, switch
statements are just shorthands for chains of conditional gotos, so your particular example would be compiled as if it was written like:
# Pseudocode
if 1 == 1 goto alpha
if 2 == 1 goto beta
alpha:
fmt.Println("1 == 1")
beta:
fmt.Println("2 == 1")
As you can see, once execution enters the alpha
case, it would just keep flowing down, ignoring the beta
label (since labels by themselves don't really do anything). The conditional checks have already happened and won't happen again.
Hence, the non-intuitive nature of fallthrough switch
statements is simply because switch
statements are thinly veiled goto statements.
答案3
得分: 3
根据语言规范:
> “fallthrough”语句将控制权转移到表达式“switch”语句中下一个case子句的第一条语句。它只能作为该子句中最后一个非空语句使用。
这似乎完美地描述了你观察到的行为。
英文:
From the language spec:
> A "fallthrough" statement transfers control to the first statement of the next case clause in an expression "switch" statement. It may be used only as the final non-empty statement in such a clause.
That seems to perfectly describe your observed behavior.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论