Conversione di Tipi in Go
In Go, le conversioni di tipo sono sempre esplicite. A differenza di altri linguaggi, Go non esegue conversioni implicite tra tipi, nemmeno tra tipi numerici compatibili. Questa scelta rende il codice piu sicuro e prevedibile.
Conversione Esplicita T(v)
La sintassi base per convertire un valore v al tipo T e T(v):
package main
import "fmt"
func main() {
var intero int = 42
var decimale float64 = float64(intero) // int -> float64
var piccolo int32 = int32(intero) // int -> int32
fmt.Println(intero) // 42
fmt.Println(decimale) // 42
fmt.Println(piccolo) // 42
}
Senza la conversione esplicita, Go genera un errore di compilazione:
var a int = 10
var b float64 = a // ERRORE: cannot use a (variable of type int) as float64
Conversioni tra Tipi Numerici
Int e Float
func main() {
// Da int a float
i := 42
f := float64(i)
fmt.Println(f) // 42
// Da float a int (troncamento, non arrotondamento)
pi := 3.99
n := int(pi)
fmt.Println(n) // 3 (la parte decimale viene troncata)
// Attenzione al troncamento
negativo := -2.7
fmt.Println(int(negativo)) // -2
}
Tra Diversi Tipi Interi
func main() {
var grande int64 = 1000
var piccolo int8 = int8(grande)
fmt.Println(piccolo) // -24 (overflow! 1000 non entra in int8)
var senza uint = 42
var con int = int(senza)
fmt.Println(con) // 42
}
Attenzione: le conversioni tra tipi numerici possono causare perdita di dati o overflow. Go non segnala errori in questi casi.
Float32 e Float64
func main() {
var f64 float64 = 3.141592653589793
var f32 float32 = float32(f64)
fmt.Printf("float64: %.15f\n", f64) // 3.141592653589793
fmt.Printf("float32: %.15f\n", f32) // 3.141592741012573 (perdita di precisione)
}
Conversione Stringa - Numero con strconv
Il pacchetto strconv fornisce funzioni per convertire tra stringhe e tipi numerici.
String a Int: strconv.Atoi
package main
import (
"fmt"
"strconv"
)
func main() {
s := "42"
n, err := strconv.Atoi(s)
if err != nil {
fmt.Println("Errore:", err)
return
}
fmt.Println(n) // 42
// Errore con stringa non valida
_, err = strconv.Atoi("abc")
fmt.Println(err) // strconv.Atoi: parsing "abc": invalid syntax
}
Int a String: strconv.Itoa
func main() {
n := 42
s := strconv.Itoa(n)
fmt.Println(s) // "42"
fmt.Printf("%T\n", s) // string
}
Conversioni con ParseInt e ParseFloat
Per maggiore controllo si usano ParseInt e ParseFloat:
func main() {
// ParseInt con base e dimensione
n, err := strconv.ParseInt("FF", 16, 64) // esadecimale, 64 bit
fmt.Println(n, err) // 255 <nil>
// ParseFloat
f, err := strconv.ParseFloat("3.14", 64)
fmt.Println(f, err) // 3.14 <nil>
// ParseBool
b, err := strconv.ParseBool("true")
fmt.Println(b, err) // true <nil>
}
Formattazione con FormatInt e FormatFloat
func main() {
// FormatInt: converte int in stringa con base
s := strconv.FormatInt(255, 16)
fmt.Println(s) // "ff"
// FormatFloat: converte float in stringa con formato
sf := strconv.FormatFloat(3.14159, 'f', 2, 64)
fmt.Println(sf) // "3.14"
}
Conversione Byte Slice e Stringa
La conversione tra []byte e string e molto comune in Go:
Da String a []byte
func main() {
s := "Ciao, Go!"
b := []byte(s)
fmt.Println(b) // [67 105 97 111 44 32 71 111 33]
// Modifica dei byte
b[0] = 'M' // modifica il primo byte
fmt.Println(string(b)) // "Miao, Go!"
}
Da []byte a String
func main() {
b := []byte{72, 101, 108, 108, 111}
s := string(b)
fmt.Println(s) // "Hello"
}
Rune e Stringhe
Le rune (rune e un alias per int32) rappresentano un singolo carattere Unicode:
func main() {
s := "Ciao"
// String a []rune
rune_slice := []rune(s)
fmt.Println(rune_slice) // [67 105 97 111]
// Singola rune a string
r := rune('A')
fmt.Println(string(r)) // "A"
// Intero a string (restituisce il carattere Unicode)
fmt.Println(string(65)) // "A"
fmt.Println(string(8364)) // carattere euro
}
Type Assertion vs Type Conversion
E importante distinguere tra type assertion e type conversion:
Type Conversion
Converte un valore da un tipo concreto a un altro tipo concreto:
var i int = 42
var f float64 = float64(i) // type conversion
Type Assertion
Estrae il tipo concreto da un’interfaccia:
var i interface{} = "ciao"
// Type assertion sicura (con controllo)
s, ok := i.(string)
if ok {
fmt.Println("E una stringa:", s)
}
// Type assertion non sicura (panic se il tipo non corrisponde)
s2 := i.(string) // OK
// n := i.(int) // PANIC: interface conversion: string, not int
Type Switch
Per controllare piu tipi possibili:
func descrivi(valore interface{}) string {
switch v := valore.(type) {
case int:
return fmt.Sprintf("Intero: %d", v)
case string:
return fmt.Sprintf("Stringa: %s", v)
case float64:
return fmt.Sprintf("Float: %.2f", v)
case bool:
return fmt.Sprintf("Booleano: %t", v)
default:
return fmt.Sprintf("Tipo sconosciuto: %T", v)
}
}
func main() {
fmt.Println(descrivi(42)) // Intero: 42
fmt.Println(descrivi("ciao")) // Stringa: ciao
fmt.Println(descrivi(3.14)) // Float: 3.14
fmt.Println(descrivi(true)) // Booleano: true
}
Conversione con fmt.Sprintf
fmt.Sprintf puo essere usato come conversione “universale” verso stringa:
func main() {
n := 42
f := 3.14
b := true
s1 := fmt.Sprintf("%d", n) // "42"
s2 := fmt.Sprintf("%.2f", f) // "3.14"
s3 := fmt.Sprintf("%t", b) // "true"
fmt.Println(s1, s2, s3)
}
Conclusione
La conversione esplicita dei tipi in Go previene errori silenziosi e rende il codice piu chiaro. Il pacchetto strconv e lo strumento principale per le conversioni tra stringhe e numeri, mentre la type assertion permette di estrarre tipi concreti dalle interfacce. Comprendere la differenza tra type conversion e type assertion, e le possibili perdite di dati nelle conversioni numeriche, e fondamentale per scrivere codice Go corretto e robusto.