Menu

Type assertion in Golang: x.(T), comma-ok e type switch

Una type assertion estrae il valore concreto da un'interfaccia. Scopri x.(T), la forma comma-ok che non va mai in panic, l'asserzione verso un'altra interfaccia, i type switch ed errors.As per gli errori incapsulati.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

x.(T): estrarre il valore da un'interfaccia

Un valore interfaccia nasconde il tipo concreto che contiene. Una type assertion x.(T) te lo restituisce.

Output:

gopher 6
0 false
string of length 6

x deve avere un tipo interfaccia. Un'asserzione su un tipo concreto non compila: invalid operation: s (variable of type string) is not an interface.

La forma a un valore va in panic

Quando l'asserzione è sbagliata, la forma a un solo valore va in panic con un messaggio che indica entrambi i tipi:

Output:

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

Usa la forma a un valore solo quando un tipo diverso sarebbe un errore di programmazione per cui vuoi che il programma si fermi. In tutti gli altri casi usa comma-ok. Se fallisce, comma-ok restituisce il valore zero di T e false.

Anche un'asserzione su un'interfaccia nil fallisce: var v any; v.(string) va in panic con interface conversion: interface {} is nil, not string, e la forma comma-ok restituisce "", false.

Asserzione verso un'altra interfaccia

T può essere un tipo interfaccia. L'asserzione riesce allora se il valore dinamico implementa T, e il risultato mantiene lo stesso valore dinamico. È così che la libreria standard verifica le funzionalità opzionali.

io.Copy verifica se la sua sorgente implementa io.WriterTo e, se sì, la usa. fmt verifica Stringer ed error. Questo schema permette a un'interfaccia piccola di restare piccola, mentre i chiamanti sfruttano comunque implementazioni più ricche.

Type switch

Quando un valore può essere di diversi tipi, un type switch sostituisce una catena di asserzioni. x.(type) è valido solo dentro uno switch.

Output:

hi
integer 7
integer 8
3.14
nil
error: boom
unhandled []int

Regole utili da conoscere:

  • In un caso con un solo tipo, x ha quel tipo. In un caso che elenca più tipi, e in default, x ha il tipo dell'interfaccia originale (qui any).
  • I casi vengono controllati in ordine. Metti le interfacce più specifiche prima di quelle più ampie, perché un valore può soddisfarne diverse.
  • case nil corrisponde solo a un'interfaccia nil, non a un'interfaccia che contiene un puntatore nil.
  • In un type switch non esiste fallthrough.

errors.As: l'asserzione per gli errori incapsulati

Gli errori vengono spesso incapsulati aggiungendo contesto: fmt.Errorf("load config: %w", err). Una type assertion diretta guarda solo l'errore più esterno e non vede quello interno. errors.As percorre la catena.

Output:

type assertion finds it: false
errors.As finds it: open /no/such/file

Usa una type assertion sugli errori solo quando sai che l'errore non è incapsulato, cioè in pratica quasi mai. La pagina sugli errori personalizzati tratta errors.As, errors.Is e la definizione dei tuoi tipi di errore.

Asserzioni, conversioni e generics

HaiVuoiUsa
intfloat64conversione: float64(n)
un any che contiene un intl'intasserzione: v.(int)
un any di diversi tipi possibilidiramare in base al tipotype switch
un error che può essere incapsulatoun tipo di errore specificoerrors.As
una funzione che funziona per molti tipisicurezza in fase di compilazionegenerics

Un codice pieno di any e type switch è spesso il segnale che i generics o un'interfaccia ben fatta esprimerebbero meglio l'intenzione e troverebbero gli errori in fase di compilazione.

Errori comuni

  • Usare la forma che va in panic su dati non affidabili. JSON decodificato, valori di mappa di tipo any, input di plugin: sempre comma-ok.
  • Asserire il tipo numerico sbagliato. I numeri JSON vengono decodificati in any come float64, quindi v.(int) fallisce su di essi.
  • Asserire un tipo valore quando è memorizzato un puntatore. Se l'interfaccia contiene *User, allora v.(User) fallisce. Asserisci v.(*User).
  • Type assertion sugli errori. Usa errors.As.

Domande frequenti

Cos'è una type assertion in Go?

È un'espressione x.(T) dove x è un valore interfaccia. Se T è un tipo concreto, restituisce il valore contenuto in x come T. Se T è un tipo interfaccia, verifica che anche il valore contenuto implementi T. La forma a un valore va in panic quando la verifica fallisce; la forma a due valori v, ok := x.(T) lo segnala in ok.

Come verifico il tipo di un valore interfaccia in Go?

Usa un type switch: switch v := x.(type) { case int: ...; case string: ...; default: ... }. Dentro ogni caso v ha il tipo di quel caso. Per un solo tipo, l'asserzione comma-ok s, ok := x.(string) è più breve. fmt.Printf("%T", x) stampa il nome del tipo per il debug.

Qual è la differenza tra type assertion e conversione di tipo in Go?

Una conversione T(x) trasforma un valore di un tipo in un altro, come float64(n); è il compilatore a decidere se è permessa, e non può fallire durante l'esecuzione. Un'asserzione x.(T) funziona solo sui valori interfaccia e verifica durante l'esecuzione quale tipo è contenuto. Non puoi scrivere int(x) per un any che contiene un int; ti serve x.(int).

Devo usare una type assertion per controllare i tipi di errore?

No. Usa errors.As(err, &target). Un'asserzione diretta err.(*MyError) guarda solo l'errore più esterno, quindi fallisce appena l'errore viene incapsulato con fmt.Errorf("...: %w", err). errors.As percorre la catena degli errori incapsulati.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA