Tipi di Dati
Il sistema di tipi di Go
Go e un linguaggio a tipizzazione statica: ogni variabile ha un tipo definito al momento della compilazione che non puo cambiare durante l’esecuzione. Il sistema di tipi di Go e progettato per essere semplice ma espressivo, con una chiara distinzione tra tipi di base, tipi composti e tipi di riferimento.
Tipi di base (basic types)
Bool
Il tipo bool rappresenta un valore booleano, che puo essere true o false:
var attivo bool = true
var completato bool = false
var vuoto bool // valore zero: false
String
Il tipo string rappresenta una sequenza immutabile di byte, tipicamente usata per il testo:
var nome string = "Mario Rossi"
var vuota string // valore zero: "" (stringa vuota)
Tipi interi
Go offre diversi tipi interi con dimensioni specifiche:
// Interi con segno
var a int8 = 127 // da -128 a 127
var b int16 = 32767 // da -32768 a 32767
var c int32 = 2147483647 // da -2^31 a 2^31-1
var d int64 = 0 // da -2^63 a 2^63-1
// Interi senza segno
var e uint8 = 255 // da 0 a 255
var f uint16 = 0 // da 0 a 65535
var g uint32 = 0 // da 0 a 2^32-1
var h uint64 = 0 // da 0 a 2^64-1
// Dimensione dipendente dalla piattaforma
var i int = 42 // 32 o 64 bit
var j uint = 42 // 32 o 64 bit
Tipi alias
Go ha due tipi alias importanti:
var lettera byte = 'A' // alias per uint8
var carattere rune = 'Z' // alias per int32 (rappresenta un code point Unicode)
Tipi floating-point
I numeri decimali sono rappresentati da due tipi:
var x float32 = 3.14 // precisione singola (32 bit)
var y float64 = 3.14 // precisione doppia (64 bit) - il piu usato
Tipi complessi
Go supporta nativamente i numeri complessi:
var c1 complex64 = 1 + 2i // parte reale e immaginaria float32
var c2 complex128 = 3 + 4i // parte reale e immaginaria float64
Tipi composti (composite types)
Array
Un array e una sequenza di lunghezza fissa di elementi dello stesso tipo:
package main
import "fmt"
func main() {
var numeri [5]int = [5]int{10, 20, 30, 40, 50}
colori := [3]string{"rosso", "verde", "blu"}
fmt.Println(numeri[0]) // 10
fmt.Println(colori[2]) // blu
fmt.Println(len(numeri)) // 5
}
La lunghezza di un array e parte del suo tipo: [3]int e [5]int sono tipi diversi.
Slice
Uno slice e una vista dinamica su un array sottostante. E il tipo piu usato per le collezioni in Go:
package main
import "fmt"
func main() {
// Creazione di uno slice
numeri := []int{1, 2, 3, 4, 5}
// Aggiungere elementi con append
numeri = append(numeri, 6, 7)
// Slicing
primi := numeri[0:3] // [1, 2, 3]
fmt.Println(numeri) // [1 2 3 4 5 6 7]
fmt.Println(primi) // [1 2 3]
fmt.Println(len(numeri)) // 7
fmt.Println(cap(numeri)) // capacita sottostante
}
Map
Una map e una collezione di coppie chiave-valore (dizionario):
package main
import "fmt"
func main() {
// Dichiarazione e inizializzazione
capitali := map[string]string{
"Italia": "Roma",
"Francia": "Parigi",
"Spagna": "Madrid",
}
// Accesso ai valori
fmt.Println(capitali["Italia"]) // Roma
// Aggiungere un elemento
capitali["Germania"] = "Berlino"
// Verificare se una chiave esiste
valore, esiste := capitali["Giappone"]
fmt.Println(valore, esiste) // "" false
// Eliminare un elemento
delete(capitali, "Francia")
}
Struct
Una struct raggruppa campi con nomi e tipi diversi:
package main
import "fmt"
type Persona struct {
Nome string
Cognome string
Eta int
}
func main() {
p := Persona{
Nome: "Mario",
Cognome: "Rossi",
Eta: 30,
}
fmt.Println(p.Nome) // Mario
fmt.Println(p.Eta) // 30
}
Tipi di riferimento
Puntatori
Un puntatore contiene l’indirizzo di memoria di un valore:
package main
import "fmt"
func main() {
x := 42
p := &x // p e un puntatore a x
fmt.Println(*p) // 42 (dereferenziazione)
*p = 100
fmt.Println(x) // 100 (x e stato modificato tramite il puntatore)
}
Funzioni come tipo
In Go le funzioni sono valori di prima classe e possono essere assegnate a variabili:
package main
import "fmt"
func main() {
somma := func(a, b int) int {
return a + b
}
risultato := somma(3, 5)
fmt.Println(risultato) // 8
}
Interfacce
Un’interfaccia definisce un insieme di metodi che un tipo deve implementare:
package main
import "fmt"
type Salutatore interface {
Saluta() string
}
type Italiano struct {
Nome string
}
func (i Italiano) Saluta() string {
return "Ciao, sono " + i.Nome
}
func main() {
var s Salutatore = Italiano{Nome: "Marco"}
fmt.Println(s.Saluta()) // Ciao, sono Marco
}
Channel
I channel sono usati per la comunicazione tra goroutine:
package main
import "fmt"
func main() {
messaggi := make(chan string)
go func() {
messaggi <- "Ciao dal channel!"
}()
msg := <-messaggi
fmt.Println(msg) // Ciao dal channel!
}
Valori zero per ogni tipo
Ogni tipo in Go ha un valore zero predefinito:
package main
import "fmt"
func main() {
var b bool // false
var i int // 0
var f float64 // 0
var s string // ""
var p *int // nil
var sl []int // nil
var m map[string]int // nil
var ch chan int // nil
var fn func() // nil
fmt.Printf("bool: %v\n", b)
fmt.Printf("int: %v\n", i)
fmt.Printf("float: %v\n", f)
fmt.Printf("string: %q\n", s)
fmt.Printf("pointer: %v\n", p)
fmt.Printf("slice: %v\n", sl)
fmt.Printf("map: %v\n", m)
fmt.Printf("channel: %v\n", ch)
fmt.Printf("func: %v\n", fn)
}
Conversione di tipo
Go non esegue conversioni implicite tra tipi. Devi sempre convertire esplicitamente:
package main
import "fmt"
func main() {
var intero int = 42
var decimale float64 = float64(intero)
var piccolo int32 = int32(intero)
fmt.Println(intero, decimale, piccolo)
// Conversione stringa/byte
testo := "ciao"
bytes := []byte(testo)
indietro := string(bytes)
fmt.Println(bytes) // [99 105 97 111]
fmt.Println(indietro) // ciao
}
Conclusione
Il sistema di tipi di Go e completo e ben organizzato. I tipi di base coprono numeri, stringhe e booleani. I tipi composti (array, slice, map, struct) permettono di organizzare dati complessi. I tipi di riferimento (puntatori, funzioni, interfacce, channel) abilitano pattern avanzati. La regola dei valori zero garantisce che ogni variabile sia sempre inizializzata in modo sicuro, e le conversioni esplicite prevengono errori subdoli legati ai tipi.