Go

Explain type switch semantics. What does this print?

Question 85MediumGo 1.22 to 1.25
func describe(x any) string {
    switch v := x.(type) {
    case nil:
        return "nil"
    case int, int64:
        return fmt.Sprintf("integer %T", v) // v has type any here
    case error:
        return "error: " + v.Error()
    case fmt.Stringer:
        return "stringer: " + v.String()
    case *bytes.Buffer:
        return "buffer" // never reached for *bytes.Buffer, see below
    default:
        return fmt.Sprintf("other %T", v)
    }
}

var p *bytes.Buffer
fmt.Println(describe(nil))            // nil
fmt.Println(describe(int64(3)))       // integer int64
fmt.Println(describe(p))              // stringer: <nil>
fmt.Println(describe(errors.New("x"))) // error: x

The rules:

  • Cases are tested in order. *bytes.Buffer implements fmt.Stringer, so the Stringer case matches first and the later concrete case is unreachable for that type. The compiler does not warn you when an interface case comes first; staticcheck's SA4020 (unreachable case clause) does.
  • In a case that lists one type, v has that type. In a case that lists several types, or in default, v keeps the original interface type.
  • case nil matches only a nil interface. A typed nil pointer matches its type case. (*bytes.Buffer).String handles a nil receiver and returns "<nil>".
  • fallthrough is not allowed in a type switch.

More on Interfaces, Methods & Embedding

All 35 Interfaces, Methods & Embedding questions