Menu

Struct in Golang: definire, inizializzare, confrontare e usare i tag

Le struct raggruppano campi con nome in un unico tipo. Scopri come definirle e inizializzarle, i valori zero, i puntatori a struct, le struct anonime, il confronto, i tag delle struct e la convenzione del costruttore NewX.

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

Definire e creare una struct

Un tipo struct è un elenco di campi con nome. Lo dichiari con type Name struct { ... } e crei i valori con un letterale composito.

Output:

Ana 31
{Name:Ana Email:ana@example.com Age:32 Admin:false}
{Name: Email: Age:0 Admin:false}
main.User{Name:"Bob", Email:"", Age:0, Admin:false}

%+v stampa i nomi dei campi e %#v stampa la sintassi Go. Sono il modo più rapido per ispezionare una struct durante il debug.

I campi dello stesso tipo possono stare sulla stessa riga: X, Y float64. I nomi dei campi con l'iniziale maiuscola sono esportati (visibili agli altri pacchetti e a encoding/json); quelli minuscoli no.

Forme dei letterali

User{Name: "Ana", Age: 31}        // named fields: order-free, omitted fields are zero
User{"Ana", "a@x.com", 31, false} // positional: every field, in order
&User{Name: "Ana"}                // pointer to a new struct
new(User)                         // pointer to a zeroed struct

Preferisci i campi con nome. I letterali posizionali si rompono quando qualcuno aggiunge o riordina un campo, e go vet li segnala per i tipi struct di altri pacchetti (per esempio net/url.Error struct literal uses unkeyed fields). La forma posizionale va bene per piccoli tipi locali come Point{1, 2}.

Niente valori di default: valori zero e costruttori

Go non ha valori di default per i campi né costruttori nel linguaggio. Ogni campo parte dal valore zero del suo tipo: 0, "", false, nil. Due idiomi colmano la lacuna.

Rendi utile il valore zero. sync.Mutex, bytes.Buffer e strings.Builder funzionano tutti senza inizializzazione. Quando puoi progettare un tipo in questo modo, fallo.

Scrivi una funzione NewX quando un campo ha bisogno di un valore di default diverso da zero o quando qualcosa va allocato, come una mappa:

NewServer restituisce un puntatore perché il server ha metodi che lo modificano e verrà condiviso. Restituire la struct per valore è comune anche per piccoli tipi immutabili (time.Date restituisce un time.Time).

Puntatori a struct

Go dereferenzia automaticamente i puntatori a struct per l'accesso ai campi. Con p := &User{} scrivi p.Name, non (*p).Name.

Output:

{11 12}
{11 12} {0 12}
{11 0}

Le struct sono valori. L'assegnazione e gli argomenti di funzione le copiano; un puntatore ne condivide una. Le copie campo per campo sono superficiali: un campo slice o mappa nella copia punta ancora agli stessi dati. Vedi puntatori per capire quando usare l'una o l'altra forma.

Struct anonime

Un tipo struct si può scrivere in linea senza un nome. È utile per raggruppamenti occasionali, test guidati da tabelle e per decodificare JSON che leggi una sola volta.

La tabella []struct{ in string; want int } è la forma standard di un test in Go.

Confrontare struct

Le struct sono confrontabili con == quando ogni campo è confrontabile. L'uguaglianza significa che tutti i campi sono uguali. Le struct confrontabili possono anche essere chiavi di una mappa.

Per una struct con campi slice o mappa, scrivi un metodo Equal oppure confronta nei test con reflect.DeepEqual.

Tag delle struct

Un tag è un letterale stringa dopo il tipo di un campo. Il linguaggio lo ignora; le librerie lo leggono con la reflection. Il lettore più comune è encoding/json:

Stampa {"id":7,"name":"Mug"}: il tag rinomina i campi, omitempty elimina il Price a zero e il campo non esportato è invisibile al codificatore. I tag seguono la convenzione key:"value", con più chiavi separate da spazi: `json:"name" db:"user_name"`. Un tag malformato compila senza problemi e viene ignorato in silenzio, cosa che go vet rileva. La pagina su json tratta le opzioni.

Struct vuote e ordine dei campi

struct{} non ha campi e occupa zero byte. Si usa come valore di un set (map[string]struct{}) e come segnale sui canali (chan struct{}), dove conta solo l'evento.

L'ordine dei campi influisce sulla disposizione in memoria: il compilatore aggiunge padding per allineare i campi. Una struct bool, int64, bool occupa 24 byte sui sistemi a 64 bit, mentre int64, bool, bool ne occupa 16. Conta solo per i tipi memorizzati in grandi quantità.

Errori comuni

  • Modificare una copia. for _, u := range users { u.Age++ } cambia ogni volta una copia. Usa l'indice: users[i].Age++.
  • Dimenticare di inizializzare un campo mappa. Il valore zero della struct contiene una mappa nil; scriverci va in panic. Allocala in un costruttore.
  • Copiare una struct che contiene un sync.Mutex. La copia ha il proprio stato del lock. Passa queste struct per puntatore; go vet segnala la copia.
  • Letterali posizionali per tipi di altri pacchetti. Si rompono quando viene aggiunto un campo.

Domande frequenti

Come si inizializza una struct in Go?

Con un letterale composito. La forma abituale usa i campi con nome: u := User{Name: "Ana", Age: 31}, e ogni campo che ometti prende il suo valore zero. User{} dà tutti valori zero, &User{...} dà un puntatore e new(User) dà un puntatore a una struct azzerata.

Go supporta valori di default per i campi di una struct?

No. Ogni campo parte dal valore zero del suo tipo. L'idioma è una funzione costruttore, func NewServer() *Server { return &Server{Port: 8080} }, oppure progettare il tipo in modo che il suo valore zero sia già utile, come succede per sync.Mutex e bytes.Buffer.

Come si confrontano due struct in Go?

Con ==, se ogni campo è confrontabile. Due valori struct sono uguali quando tutti i campi corrispondenti sono uguali. Una struct che contiene un campo slice, mappa o funzione non si può confrontare con == (errore di compilazione); confronta tu i campi oppure usa reflect.DeepEqual nei test.

Cosa sono i tag delle struct in Go?

Letterali stringa dopo il tipo di un campo, come `json:"name,omitempty"`. Il compilatore li ignora; pacchetti come encoding/json, i driver di database e i validatori li leggono con la reflection per controllare come i campi vengono codificati, nominati o verificati.

Come stampo una struct con i nomi dei campi in Go?

Usa fmt.Printf("%+v\n", s), che stampa {Name:Ana Age:31}. %v stampa solo i valori, e %#v stampa la sintassi Go compreso il nome del tipo: main.User{Name:"Ana", Age:31}.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA