switch vs match
PHP has two of these. switch compares loosely and falls through unless you remember break. match, from PHP 8, compares strictly, never falls through, and returns a value. Go's switch sits between them, a lot nearer to match.
No breaks to forget
switch line { case "Central": colour = "red" case "Jubilee": colour = "grey" default: colour = "black" }
Each case ends where the next one starts. There's no break, because there's no fallthrough to stop.
| case 'Central' | $colour = 'red'; and no break |
|---|---|
| case 'Jubilee' | $colour = 'grey'; runs as well |
| Bug report | "Why is the Central line grey?" |
A few more things that work like match:
- Several values share a case with commas:
case 301, 302, 307, 308:. - Comparisons are strict. Both sides must be the same type, so
"1"never matches1, and the compiler stops you trying. - Cases are tried top to bottom, and the first match wins.
defaultcan go anywhere, but it only runs if nothing else matches. - Cases can be any expression of the right type, not only literals:
case maxDelay:orcase limit + 1:.
If you really do want PHP's fallthrough, Go has a keyword for it: end a case with fallthrough, and the next case's body runs too, without checking its value. You'll almost never see it.
The difference shows up the moment a case matches:
%%{init: {"flowchart": {"nodeSpacing": 18}}}%%
flowchart TB
accTitle: What happens after a case matches, in PHP and in Go
accDescr: In PHP, when case 301 matches, its empty body runs and then execution falls into case 302's body, and on through every case below until it reaches a break. In Go, when case 301 matches, only its own body runs, which is empty, and then the switch ends.
subgraph go ["Go"]
direction TB
g1["301 matches"] --> g2["empty body runs"] --> g3["switch ends"]
end
subgraph php ["PHP"]
direction TB
p1["301 matches"] --> p2["empty body runs"] --> p3["falls into 302"] --> p4["…to a break"]
end
Mind the gap
An empty case does nothing. In PHP, stacking case 301: case 302: with no body lets 301 fall through to 302's code. In Go, case 301: with no body matches 301, runs nothing and leaves the switch. It compiles without a word. List the values together instead: case 301, 302:.
Where it's not match
Go's switch is a statement, not an expression. There's no $label = match (…) { … }, so you assign inside each case, or put the switch in a small function and return from each case. That second option reads almost exactly like a match.
And when nothing matches and there's no default, nothing happens. PHP's match would throw an UnhandledMatchError. Go just carries on after the switch, so if a missing case would be a bug, add a default that says so.
The tagless switch
Leave out the value after switch, and each case becomes a condition of its own:
switch { case delay <= 0: band = "On time" case delay < 15: band = "Late" default: band = "Very late" }
The first case that's true runs. This is PHP's match (true) trick, built in, and it's the usual Go replacement for a long if / else if chain. You can't mix the two styles: a switch code compares every case with code, so a condition like case code >= 500: in one is a compile error.
Like if (1.8), a switch can run a short statement first, switch s := status(); s { … }, with s scoped to the switch.
break in a switch
break works inside a switch, though you rarely need it. The catch is that it leaves the innermost for or switch. Inside a loop, a break in a case leaves the switch, and the loop carries on. To leave the loop, label it and use break label, as in 1.9.
(There's one more kind, the type switch, which picks a case based on a value's type. It needs interfaces, so it waits for lesson 3.6.)
Recap
- No fallthrough and no
breakneeded. An empty case does nothing. case a, b, c:shares a case. Comparisons are strict, and cases are tried in order.- A switch is a statement, not an expression. Assign or return in each case. No match means no error.
switch { case cond: … }is the tagless switch: Go'smatch (true)andif / else ifreplacement.breakleaves the switch, not the surrounding loop.