type assertion în Go

Scoate valoarea concretă dintr-o interfață: x.(T) verifică dacă interfața ține tipul T și îl returnează.

Sintaxă
v := x.(T)         // panic dacă x nu ține un T
v, ok := x.(T)     // ok = false, fără panic

Ce face type assertion

O valoare de tip interfață (de exemplu any sau error) ascunde tipul concret. x.(T) verifică la rulare dacă x ține o valoare de tipul T și o returnează ca T. Dacă T este tot o interfață, verifică dacă valoarea are metodele ei.

Forma cu o singură valoare oprește programul cu panic dacă tipul nu se potrivește. Forma cu două valori, v, ok := x.(T), nu dă panic: ok este false, iar v primește valoarea zero a lui T. Folosește forma cu ok ori de câte ori nu ești sigur.

Pentru mai multe tipuri posibile, un type switch este mai clar decât o serie de assertion-uri. Pentru erori, errors.As este preferat lui err.(*TipEroare), pentru că verifică și erorile împachetate.

Exemple

Exemplul 1: Forma sigură, cu ok

package main

import "fmt"

func main() {
    var valori = []any{42, "Iași", 3.14, true}
    for _, v := range valori {
        if s, ok := v.(string); ok {
            fmt.Printf("text de %d octeți: %s\n", len(s), s)
        } else if n, ok := v.(int); ok {
            fmt.Println("întreg, dublul este", n*2)
        } else {
            fmt.Printf("alt tip: %T\n", v)
        }
    }
}

Rezultat

întreg, dublul este 84
text de 5 octeți: Iași
alt tip: float64
alt tip: bool

Exemplul 2: Verificarea unei interfețe opționale

Verificăm dacă valoarea are și metoda Reducere.

package main

import "fmt"

type Produs interface{ Pret() float64 }
type CuReducere interface{ Reducere() float64 }

type Carte struct{}
type Telefon struct{}

func (Carte) Pret() float64       { return 50 }
func (Telefon) Pret() float64     { return 2000 }
func (Telefon) Reducere() float64 { return 0.15 }

func main() {
    for _, p := range []Produs{Carte{}, Telefon{}} {
        pret := p.Pret()
        if r, ok := p.(CuReducere); ok {
            pret *= 1 - r.Reducere()
        }
        fmt.Printf("%T: %.2f lei\n", p, pret)
    }
}

Rezultat

main.Carte: 50.00 lei
main.Telefon: 1700.00 lei

Exemplul 3: Forma fără ok dă panic

Panic-ul este oprit cu recover doar ca să putem afișa mesajul.

package main

import "fmt"

func main() {
    defer func() {
        fmt.Println("panic:", recover())
    }()
    var x any = "text"
    n := x.(int)
    fmt.Println(n)
}

Rezultat

panic: interface conversion: interface {} is string, not int

Greșeli frecvente

  • x.(T) fără ok pe date venite din exterior (JSON decodat în any): un tip neașteptat oprește programul.
  • Assertion pe o variabilă care nu este interfață: invalid operation: n (variable of type int) is not an interface.
  • Numerele din JSON decodat în any sunt float64, nu int: v.(int) eșuează.

Comenzi înrudite

  • type switch: Un switch care alege ramura după tipul concret ținut de o interfață, cu variabila deja convertită.
  • 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.
  • erori (error): În Go erorile sunt valori: funcțiile returnează un error, iar apelantul verifică if err != nil.