Type switch puzzle: what does describe return for each input?
func describe(x any) string {
switch v := x.(type) {
case nil:
return "nil"
case int, int64:
// return fmt.Sprint(v + 1) // compile error: v is any here
return fmt.Sprintf("integer %v %T", v, v)
case error:
return "error " + v.Error()
case fmt.Stringer:
return "stringer " + v.String()
default:
return fmt.Sprintf("other %T", v)
}
}
var p *int
fmt.Println(describe(nil)) // ?
fmt.Println(describe(int64(5))) // ?
fmt.Println(describe(p)) // ?
fmt.Println(describe(time.Second)) // ?
fmt.Println(describe(errors.New("x"))) // ?
Output:
nil
integer 5 int64
other *int
stringer 1s
error x
Points the interviewer is checking:
case nil matches only a nil interface. A typed nil pointer such as p has dynamic type *int, so it falls to default. This is the same typed-nil trap as with error.
In a case that lists one type, v has that type. In a case that lists several types (case int, int64), and in default and case nil, v has the type of the switch expression, here any. That is why v + 1 does not compile there.
Cases are tried in source order, and the first match wins. time.Duration has a String method but no Error method, so it reaches the Stringer case. A type that implements both error and Stringer always stops at error, because that case comes first. Put the more specific interfaces first. fmt follows the same rule: it prefers Error() over String() when formatting with %v.
More on Tricky Output & Code-Review Puzzles
- Q512Deleting from a slice in place: what do both lines print?
- Q513Embedding is not inheritance: what does this print, and which line does not compile?
- Q515Package initialization order: what does this program print?