Uno dei concetti fondamentali in Go è quello degli struct, che permettono di creare tipi di dati personalizzati per organizzare e gestire informazioni complesse. In questo articolo, esploreremo cosa sono gli struct, come si dichiarano, come si utilizzano e quali sono le best practices per lavorare con essi in Go.

1. Cosa sono gli Struct in Go?

Gli struct (abbreviazione di “structure”) sono tipi di dati personalizzati che permettono di raggruppare più valori sotto un unico nome. Ogni valore all’interno di uno struct è chiamato campo, e ogni campo ha un nome e un tipo. Gli struct sono simili alle classi in altri linguaggi, ma in Go sono più leggeri e non supportano l’ereditarietà.

1.1. Dichiarazione di uno Struct

Per dichiarare uno struct, si utilizza la parola chiave type seguita dal nome dello struct e dalla parola chiave struct. Ecco un esempio:

type Persona struct {
    Nome string
    Eta  int
    Attivo bool
}

In questo esempio, abbiamo definito uno struct chiamato Persona con tre campi: Nome (di tipo string), Eta (di tipo int) e Attivo (di tipo bool).

1.2. Creazione di un’Istanza di Struct

Per creare un’istanza di uno struct, si utilizza la sintassi seguente:

persona1 := Persona{
    Nome: "Alice",
    Eta: 30,
    Attivo: true,
}

In questo caso, persona1 è un’istanza dello struct Persona con i valori specificati per ciascun campo.

1.3. Accesso ai Campi di uno Struct

Per accedere ai campi di uno struct, si utilizza la notazione del punto (.):

fmt.Println("Nome:", persona1.Nome)
fmt.Println("Età:", persona1.Eta)
fmt.Println("Attivo:", persona1.Attivo)

Output:

Nome: Alice
Età: 30
Attivo: true

2. Struct Mutabili

Per modificare i campi di uno struct, l’istanza deve essere dichiarata come mutabile:

persona1.Eta = 31 // Modifica del campo "Eta"
persona1.Attivo = false // Modifica del campo "Attivo"

Se l’istanza non è dichiarata come mutabile, non sarà possibile modificare i suoi campi.

3. Struct con Campi Opzionali

In Go, non esiste un concetto di “campi opzionali” direttamente negli struct. Tuttavia, è possibile utilizzare il tipo pointer per rappresentare campi che possono essere assenti:

type Persona struct {
    Nome string
    Eta  *int
    Attivo bool
}

eta := 25
persona1 := Persona{
    Nome: "Bob",
    Eta: &eta,
    Attivo: true,
}

In questo esempio, il campo Eta è opzionale e può contenere un valore di tipo int o nil.

4. Metodi e Funzioni Associate

Gli struct in Go possono avere metodi, che sono funzioni che operano su un’istanza di uno struct. I metodi sono definiti con un ricevitore, che specifica su quale tipo di struct operano.

4.1. Definizione di Metodi

Ecco un esempio di metodo associato a uno struct:

func (p Persona) Saluta() {
    fmt.Printf("Ciao, mi chiamo %s e ho %d anni.\n", p.Nome, p.Eta)
}

persona1.Saluta() // Output: Ciao, mi chiamo Alice e ho 30 anni.

4.2. Metodi con Ricevitori Puntatori

Per modificare i campi di uno struct all’interno di un metodo, è necessario utilizzare un ricevitore puntatore:

func (p *Persona) CompieAnni() {
    p.Eta++
}

persona1.CompieAnni()
fmt.Println("Nuova età:", persona1.Eta) // Output: Nuova età: 31

5. Best Practices per Lavorare con gli Struct

Ecco alcune best practices per lavorare con gli struct in Go:

5.1. Usa Nomi Descrittivi per i Campi

Assegna nomi descrittivi ai campi degli struct per migliorare la leggibilità del codice. Ad esempio, usa Nome invece di n.

5.2. Utilizza Metodi per Incapsulare la Logica

Invece di manipolare direttamente i campi di uno struct, definisci metodi per incapsulare la logica. Questo rende il codice più modulare e facile da mantenere.

5.3. Evita Struct Troppo Grandi

Se uno struct ha troppi campi, considera di suddividerlo in più struct più piccoli. Questo migliora la leggibilità e la manutenibilità del codice.

5.4. Usa Puntatori per Campi Opzionali

Se un campo può essere opzionale, utilizza un puntatore per rappresentarlo. Questo permette di distinguere tra un valore zero e un campo non impostato.

6. Esempi Pratici

Vediamo alcuni esempi pratici di come utilizzare gli struct in Go.

6.1. Gestione di un Catalogo di Prodotti

Ecco un esempio di come utilizzare uno struct per rappresentare un prodotto in un catalogo:

type Prodotto struct {
    Nome string
    Prezzo float64
    Quantita int
}

func (p Prodotto) Totale() float64 {
    return p.Prezzo * float64(p.Quantita)
}

prodotto1 := Prodotto{Nome: "Laptop", Prezzo: 999.99, Quantita: 2}
fmt.Println("Totale:", prodotto1.Totale()) // Output: Totale: 1999.98

6.2. Gestione di un Sistema di Autenticazione

Ecco un esempio di come utilizzare uno struct per rappresentare uno stato di autenticazione:

type Autenticazione struct {
    NomeUtente string
    Token string
    Scadenza time.Time
}

func (a Autenticazione) Valida() bool {
    return time.Now().Before(a.Scadenza)
}

autenticazione1 := Autenticazione{
    NomeUtente: "Alice",
    Token: "abc123",
    Scadenza: time.Now().Add(24 * time.Hour),
}

fmt.Println("Autenticazione valida:", autenticazione1.Valida())

7. Conclusioni

Gli struct sono uno strumento potente in Go per organizzare e gestire dati complessi. Con questa guida, hai imparato come dichiarare, creare e utilizzare struct, oltre a esplorare funzionalità avanzate come metodi e best practices. Gli struct sono fondamentali per scrivere codice Go modulare e sicuro, e ora sei pronto per utilizzarli nei tuoi progetti!