type switch în Go

Un switch care alege ramura după tipul concret ținut de o interfață, cu variabila deja convertită.

Sintaxă
switch v := x.(type) {
case int:
    // v este int
case string, []byte:
    // v rămâne de tipul lui x
case nil:
    ...
default:
    ...
}

Ce face type switch

switch v := x.(type) verifică tipul concret al valorii din interfața x și execută ramura potrivită. În fiecare ramură cu un singur tip, v are deja acel tip, deci îl poți folosi direct (v * 2 pentru int, len(v) pentru string).

O ramură poate lista mai multe tipuri (case int, int64:); atunci v rămâne de tipul interfeței. case nil: prinde interfața goală, iar default orice alt tip. Ramurile pot fi și interfețe (case fmt.Stringer:). Cuvântul .(type) se poate folosi doar într-un switch, iar fallthrough nu este permis.

Exemple

Exemplul 1: Descrierea unor valori de tipuri diferite

package main

import "fmt"

func descrie(x any) string {
    switch v := x.(type) {
    case nil:
        return "nimic"
    case int:
        return fmt.Sprintf("întreg, pătratul este %d", v*v)
    case float64:
        return fmt.Sprintf("real, jumătatea este %.2f", v/2)
    case string:
        return fmt.Sprintf("text cu %d octeți", len(v))
    case []int:
        return fmt.Sprintf("listă cu %d numere", len(v))
    case bool, error:
        return fmt.Sprintf("bool sau eroare: %v", v)
    default:
        return fmt.Sprintf("tip necunoscut %T", v)
    }
}

func main() {
    for _, x := range []any{7, 2.5, "Brașov", []int{1, 2, 3}, true, nil, 'a'} {
        fmt.Println(descrie(x))
    }
}

Rezultat

întreg, pătratul este 49
real, jumătatea este 1.25
text cu 7 octeți
listă cu 3 numere
bool sau eroare: true
nimic
tip necunoscut int32

Exemplul 2: Ramuri cu interfețe

Ramurile se verifică în ordine; un tip care are String() intră pe ramura fmt.Stringer.

package main

import (
    "errors"
    "fmt"
)

type Oras struct{ Nume string }

func (o Oras) String() string { return "orașul " + o.Nume }

func afiseaza(x any) {
    switch v := x.(type) {
    case error:
        fmt.Println("eroare:", v.Error())
    case fmt.Stringer:
        fmt.Println("stringer:", v.String())
    default:
        fmt.Println("valoare:", v)
    }
}

func main() {
    afiseaza(Oras{"Sibiu"})
    afiseaza(errors.New("fișier lipsă"))
    afiseaza(100)
}

Rezultat

stringer: orașul Sibiu
eroare: fișier lipsă
valoare: 100

Greșeli frecvente

  • x.(type) în afara unui switch: invalid syntax tree: use of .(type) outside type switch.
  • Așteptarea ca v să aibă tipul concret într-o ramură cu mai multe tipuri: rămâne interfață.
  • fallthrough într-un type switch: nu este permis.

Comenzi înrudite

  • type assertion: Scoate valoarea concretă dintr-o interfață: x.(T) verifică dacă interfața ține tipul T și îl returnează.
  • switch, case și default: Alege una din mai multe ramuri după valoarea unei expresii sau după prima condiție adevărată.
  • interface și interfețe: Interfața descrie un set de metode; orice tip care le are o implementează automat, fără declarație explicită.
  • any: Alias pentru interface{}: un tip care poate ține orice valoare; folosit și drept constrângere în generics.