Type switches
In the previous lesson a type assertion asked a single question, "Are you a driver?", and that was enough. But if a newcomer may turn out to be a driver, a cook or an accountant, the questions pile up. Picture an HR desk at the entrance: they see who has come and send each person to their department—the driver to the garage, the cook to the kitchen.
Without such a desk, assertions with ok line up into a chain. Each new type adds another else if branch with its own check:
package main
import "fmt"
func main() {
var v any = "Go"
if n, ok := v.(int); ok {
fmt.Println("int", n)
} else if s, ok := v.(string); ok {
fmt.Println("string", len(s)) // string 2
}
}Let's go through the checks in order:
- The first asks whether
vholds anintand getsfalse. - The second finds a string and prints
string 2.
With two types the chain is still readable, but with five it stretches across the screen. A type switch writes the same check more compactly, and each new type adds one case line to it.
The v.(type) form
A type switch compares the dynamic type of a value with the types in its case branches. This is the HR desk at the entrance. The header switch x := v.(type) has these parts:
switch—the keyword, as in an ordinaryswitch;x :=—a variable that gets the value fromvin each branch;v.(type)—where the assertionv.(int)would be, the keywordtypestands.
The branches are checked from top to bottom, and the first match runs, as in an ordinary switch. The function size returns the number itself for an int and the length for a string or a slice:
func size(v any) int {
switch x := v.(type) {
case int:
return x
case string:
return len(x)
case []int:
return len(x)
case nil:
return 0
default:
return -1
}
}Two rules of the form:
- if you do not need the values, you can skip the variable:
switch v.(type); - the form
v.(type)is allowed only in aswitchheader; anywhere else the program does not compile.
The variable in each branch
Which type x gets depends on the kind of branch. Let's go through the three kinds in the function size.
A single-type branch
In such a branch, the variable x gets the branch's type. In case int, x has type int, so it can be returned as a number. In case string, x has type string, so len applies to it. Whoever reaches the garage is certainly a driver, and they are treated as one.
The case nil branch
This branch runs when nothing was put into v. Here x keeps the type any.
Remember the pair from the previous lesson: a nil pointer does not land in case nil. For var p *int, the call size(p) returns -1, because the dynamic type *int is already in the interface value.
The default branch
The default branch runs for every other type not listed in a case. Here x also keeps the type any, since any type could have landed there.
Trying it on values
Let's try the function on different values:
fmt.Println(size(7), size("Go"), size([]int{1, 2, 3})) // 7 2 3
fmt.Println(size(nil), size(2.5)) // 0 -1Let's go through each call:
size(7)returns the number as is:7.size("Go")takes the string's length:2.size([]int{1, 2, 3})takes the length of a three-element slice:3.size(nil)lands incase niland gives0.2.5has typefloat64, and there is no such branch, sodefaultruns and returns-1.
Common mistake
One branch listed two types and applied arithmetic to x:
var v any = 21
switch x := v.(type) {
case int, float64:
fmt.Println(x * 2) // ✗
}invalid operation: x * 2 (mismatched types any and untyped int)When a case lists more than one type, the compiler does not know which of them x will hold. So in such a branch x stays of type any, and it cannot be multiplied. Here the HR-desk picture ends: a person behind a shared door can be asked again, but the compiler decides everything at build time. Split the types into separate branches, case int and case float64.
Key points
switch x := v.(type)picks a branch by the dynamic type ofv; branches are checked from top to bottom.- In a single-type branch
xhas that type; incase nil,defaultand a multi-type branch it has the typeany. case nilruns only when the interface holds nothing; anilpointer does not land there.- The form
v.(type)is allowed only in aswitchheader.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.