Cos'è la somma di tipi e come implementarla in Go?
sobes.tech AI
Risposta dell'AI
La somma di tipi (o unione etichettata, unione disgiunta) è un tipo di dato che può assumere il valore di una delle diverse opzioni predefinite. A differenza del prodotto di tipi (strutture), dove il valore contiene tutti i campi contemporaneamente, la somma di tipi contiene solo uno dei valori possibili.
In Go, non c'è supporto diretto per le somme di tipi come in linguaggi come Haskell o Rust (enum). La somma di tipi può essere emulata in diversi modi:
-
Interfacce e asserzione di tipo (Type Assertion):
Creare un'interfaccia che tutte le possibili varianti implementano. Il valore della somma di tipi sarà questa interfaccia, e per determinare la variante concreta si usa l'asserzione di tipo o
type switch.package main import "fmt" // Shape - interfaccia che rappresenta la somma di tipi type Shape interface { Area() float64 } // Circle - una delle varianti type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius } // Rectangle - un'altra variante type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func main() { shapes := []Shape{Circle{Radius: 5}, Rectangle{Width: 3, Height: 4}} for _, s := range shapes { // Uso di type switch per determinare la variante switch v := s.(type) { case Circle: fmt.Printf("Cerchio con raggio %.2f, area: %.2f\n", v.Radius, v.Area()) case Rectangle: fmt.Printf("Rettangolo con dimensioni %.2f x %.2f, area: %.2f\n", v.Width, v.Height, v.Area()) default: fmt.Println("Forma sconosciuta") } } } -
Strutture con flag booleani (usate raramente):
La struttura contiene campi per tutte le varianti possibili e flag booleani per indicare quale variante è attiva. È insicuro e difficile da mantenere.
package main import "fmt" type Result struct { Value int // campo per risultato di successo Err error // campo per errore IsValue bool // flag che indica se Value è attivo IsErr bool // flag che indica se Err è attivo } func Process(input int) Result { if input > 0 { return Result{Value: input * 2, IsValue: true} } return Result{Err: fmt.Errorf("numero negativo: %d", input), IsErr: true} } func main() { res1 := Process(10) if res1.IsValue { fmt.Printf("Risultato: %d\n", res1.Value) } else if res1.IsErr { fmt.Printf("Errore: %v\n", res1.Err) } res2 := Process(-5) if res2.IsValue { fmt.Printf("Risultato: %d\n", res2.Value) } else if res2.IsErr { fmt.Printf("Errore: %v\n", res2.Err) } } -
Strutture con valori nulli per campi non attivi (spesso usate per Optional/Result):
La struttura contiene campi per tutte le varianti, ma solo uno di essi sarà non nullo. Funziona bene con puntatori o tipi con un valore "null" ben definito.
package main import "fmt" // Option - emulazione di Some/None del tipo opzionale type Option struct { Value *int // Non nullo se c'è un valore } // None crea un valore Option nullo func None() Option { return Option{} } // Some crea un Option con valore func Some(val int) Option { return Option{Value: &val} } func main() { opt1 := Some(10) if opt1.Value != nil { fmt.Printf("Valore: %d\n", *opt1.Value) } else { fmt.Println("Nessun valore") } opt2 := None() if opt2.Value != nil { fmt.Printf("Valore: %d\n", *opt2.Value) } else { fmt.Println("Nessun valore") } }
Il modo più idiomatico e sicuro per emulare somme di tipi in Go è usare interfacce e type switch. Questo garantisce che solo una delle varianti sia presente e fornisce sicurezza di tipo nel lavorarci.