Type Switch
An interface value carries two things: a concrete type and a value of that type. A type switch is how you get them back out.
switch v := v.(type) {
case int:
// v is an int here
case string:
// v is a string here
case nil:
// the interface held nothing at all
default:
// v is still the original interface
}
The v := shadows the parameter deliberately, so each case works with the
narrow type and the name stays the same. In default there is nothing to
narrow to, so v keeps its interface type - which is what %T and %v want
anyway.
Order decides
Cases are tried top to bottom, and a case naming an interface matches anything satisfying it. So this order:
case fmt.Stringer:
case string:
is a bug if any of your strings have a String method: the first case swallows
them. Put concrete types first and interfaces last, exactly like a switch on
values puts its narrow conditions before its broad ones.
A named type is its own type. type Celsius float64 does not match
case float64; it matches case Celsius, and it matches
case fmt.Stringer if it has the method.
There is no implicit widening anywhere in Go, and this is the same rule showing
up again.
One case, several types
case int, int64:
// v is still `any` here
When a case lists more than one type there is no single type to narrow to, so
v stays as the interface. Write the branches separately when each needs its
own conversion.
Assertion or switch
For one type, an assertion is shorter and says more:
n, ok := v.(int)
Reach for a type switch when there are several answers to distinguish. Reach for neither when an ordinary interface method would do - a type switch over your own types tends to mean the behaviour belongs on the types as a method.
Task
Implement String on Celsius, then Describe, SumNumbers, AsInt and
JoinStrings.
Hints
Describe'scase nilmatches an interface holding nothing. It cannot be written asif v == nilinside another case.%qquotes a string,%tprints a bool,%.2fgives two digits, and%Tprints the type name -main.Point, with the package qualifier.- Put
case fmt.Stringerafter the concrete cases, orCelsiusnever reaches it... and check the reverse too: a plainfloat64must not reach it. AsIntis one line:n, ok := v.(int). Anint64holding 7 is not anint.JoinStringscan collect into a[]stringand finish withstrings.Join, which handles the separator for you.