Type switches
…
TypeSwitchStmt = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" . TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" . TypeCaseClause = TypeSwitchCase ":" StatementList . TypeSwitchCase = "case" TypeList | "default" .
The TypeSwitchGuard may include a short variable declaration. When that form is used, the variable is declared at the end of the TypeSwitchCase in the implicit block of each clause. In clauses with a case listing exactly one type, the variable has that type; otherwise, the variable has the type of the expression in the TypeSwitchGuard.
The temporary variable created with a short variable declaration in a type switch is incredibly powerful. But there are also some subtleties. To explain what the above paragraph is describing, let’s look at a simple example.
switch t := x.(type) {
case string
/* here t is of type string */
case int:
/* here t is of type int */
default:
/* here t is of the same interface type as x */
}
So that seems pretty straight forward… what subtleties could exist?
Let’s imagine we’re trying to convert from an arbitrary interface value to an integer:
func toInt(x any) int {
switch t := x.(type) {
case int:
return t
case int8:
return int(t)
case int16:
return int(t)
/* .. skip similar cases for int32, int64, uint8, float32, etc... */
default:
panic(fmt.Sprintf("cannot convert type %T to int", x))
}
}
Notice that except for case int
, every other case (included the many omitted cases) are identical. One might feel justified in trying to shorten this, therefore, to:
func toInt(x any) int {
switch t := x.(type) {
case int:
return t
case int8, int16, int32, int64, uint8, uint16, uint32, uint64, uint, float32, float64:
return int(t)
default:
panic(fmt.Sprintf("cannot convert type %T to int", x))
}
}
Looks a lot better, right?
cannot convert t (variable of type any) to type int: need type assertion
This is because a case
clause with multiple types cannot assign a single type to t
, so it reverts to the same type as x
—the same behavior as the default
clause.
So in a case like this, you’ll be left writing a bunch of otherwise redundant case
clauses.
Quotes from The Go Programming Language Specification Language version go1.22 (Feb 6, 2024)