skip to content

In a Go switch statement, what happens at the end of a case body, and what does `fallthrough` do?

level: juniorimportance: must knowfreq 70%

answer

  1. C's default is inverted here
  2. no break statement is ever needed
  3. one keyword opts back in, explicitly
  4. it enters the next body untested

basics

~20 s

In Go a case body ends the switch automatically — there is no implicit fall-through and no break is needed. The fallthrough keyword, written as the last statement of a case, forces control into the next case's body.

solid answer

~50 s

Go inverts C's default. When a `case` body finishes, control leaves the `switch` entirely — you never write `break` to stop it, and an empty case body simply does nothing. Cases are tested in source order and the first match wins; one case can list several comma-separated values (`case 1, 2, 3:`), which covers most of what C programmers used stacked empty labels for. When you genuinely want the next clause's body to run as well, you write `fallthrough` as the final statement of the clause. Two details matter in an interview: `fallthrough` jumps into the next clause's body *without evaluating that clause's expression*, so it can enter a case whose value does not match the tag at all; and it is illegal in the last clause, because there is no following body. `default` may sit anywhere among the clauses and runs only when nothing matched.

code

go · 12 lines
go
switch x := 5; x {
case 5:
	fmt.Println("five")
	fallthrough
case 99:
	fmt.Println("ninety-nine") // runs anyway: the case is not re-tested
case 100:
	fmt.Println("hundred")
}
// prints:
// five
// ninety-nine

go deeper

for a junior

Be ready to state that a Go case body ends the switch on its own, that break is never required, and that one case may list several comma-separated values.

for a middle

Explain that fallthrough must be the final statement of a clause, that it enters the next body without evaluating that clause's expression, and that it is rejected in the last clause.

for a senior

In review, show you can spot a switch ported from C whose stacked labels became separate Go cases and quietly lost a deliberate fall-through, and argue when comma-separated values beat a fallthrough chain.

for a principal

Own the convention. Decide whether fallthrough is permitted in your codebase at all, since a chain of them makes a dispatch switch fragile for every later editor who inserts a clause in the middle.

## The default behaviour A Go `switch` has two forms; this question is about the *expression switch*, the one with a tag: ``` switch kind { case "ident": ... case "number", "string": ... default: ... } ``` The tag (`kind`) is evaluated once. Then each case's expressions are evaluated in source order, top to bottom and left to right within a clause, and compared for equality with the tag. The first equal one wins, its body runs, and **when that body finishes, the switch is over**. Control resumes at the statement after the closing brace. You do not write `break`, and writing one changes nothing except noise. This is the single biggest difference from C, C++, Java and JavaScript, where a case label is just a jump target and control keeps running straight through into the next label's statements unless a `break` stops it. Go's designers judged that the C behaviour is wrong far more often than it is wanted — the missing `break` is one of the oldest bug classes in the C family — so they flipped the default and made the rare case explicit. ## Grouping values Because an empty case body in Go does *nothing* rather than falling through, the C idiom of stacking bare labels does not translate. Go's replacement is a comma-separated list inside one case: ``` case 'a', 'e', 'i', 'o', 'u': return true ``` This is the form to reach for when several values share a body, and it is why real Go code needs `fallthrough` far less often than a C programmer expects. ## What `fallthrough` actually does `fallthrough` transfers control to **the first statement of the next case clause's body**. Three rules govern it: 1. It must be the **final statement** of the clause. It is not a conditional jump you can put in the middle of a body; `if x { fallthrough }` does not compile. 2. It is **illegal in the last clause** of the switch — there is no next body to enter. 3. It **does not evaluate the next clause's case expression**. This surprises people: falling through from `case 5:` into `case 99:` runs the second body even though the tag is 5. `fallthrough` is a jump, not a re-test. That third rule is the whole semantic. If you want the next clause's condition checked, you do not want `fallthrough`; you want two separate tests, or an expressionless switch whose cases are boolean. ## Cases need not be constants In a tagged switch, each case expression only has to be *comparable* to the tag's value. Variables and function calls are legal: ``` switch n { case lo: ... case hi(), lo + 1: ... } ``` They are evaluated in order until one matches, so a case with a side effect can be skipped entirely. Only duplicate **constant** cases are rejected at compile time (`duplicate case in switch`); two variables that happen to be equal at run time are fine, and the earlier clause wins. ## `default` A switch may have at most one `default` clause, and it may be written first, last, or in the middle — its position does not change behaviour. It runs only when no case matched. Placing it last is convention, not a rule. A `default` reached by accident is where an unhandled value goes to die quietly, which is why a switch that maps a value to a result usually wants a `default` that panics or returns an error rather than an empty one. ## The porting trap The practical failure for someone arriving from another language is the mirror image of the classic C bug. In C you forget `break` and get accidental fall-through. In Go you port that same switch, drop the `break` statements because Go does not need them, and lose the *deliberate* fall-through that a couple of stacked labels were quietly providing. The compiler cannot help: the code is valid, it just handles one token kind less than the original did. Reading a ported switch specifically for lost fall-through — and for stacked labels that should have become one comma-separated case — is a real review task, not a theoretical one. ## Summary No implicit fall-through, no `break` needed, first match wins, one case may list many values, `fallthrough` is an explicit last-statement jump into the next body without re-testing it, and it cannot appear in the final clause.

  • Can a case in a Go switch list an expression that is not a constant?
    Yes. In a switch with a tag, each case expression only has to be comparable to the tag's value, so `case lo, hi:` with variables is legal. Cases are evaluated top to bottom, left to right, until one matches. Only duplicate *constant* cases are a compile error; two variables that happen to hold the same value at run time are fine, and the earlier clause wins.
  • What happens if a case body is empty?
    Nothing runs and the switch ends. An empty clause is not a fall-through, which is the opposite of C, where stacking empty labels is the standard way to group values. In Go you group by listing the values in one clause: `case 'a', 'e', 'i':`. If you actually want the next clause's body to run, you must write `fallthrough` explicitly.
  • Where can `default` appear among the clauses, and how many can there be?
    `default` may be written anywhere — first, last, or in the middle — and its position does not change behaviour: it runs only when no case matched. A switch may have at most one, and a second is a compile error. Putting it last is convention, not a rule.

C leaves every door between rooms open and expects you to close each one; Go keeps them shut and gives you one key you must use on purpose.

saying these in an interview costs you the question

  • Claims you must write break at the end of each case
  • Says fallthrough re-tests the next case's expression
  • Believes fallthrough may appear anywhere in a case body
  • Thinks an empty case body falls through to the next one
  • Assumes a case can only list a single value