Conversione di Tipi in Rust
Rust non esegue conversioni implicite tra tipi. Ogni conversione deve essere esplicita, garantendo sicurezza e chiarezza nel codice.
Casting con as
Il keyword as converte tra tipi primitivi:
fn main() {
// Numeri
let x: i32 = 42;
let y: f64 = x as f64; // i32 → f64
let z: u8 = x as u8; // i32 → u8 (troncamento se fuori range)
let grande: i64 = 1_000_000;
let piccolo: i16 = grande as i16; // Attenzione: overflow silenzioso!
// Char e numeri
let c = 'A';
let n = c as u32; // 65
let c2 = 65u8 as char; // 'A'
// Bool
let b = true as i32; // 1
}
Attenzione: as può causare perdita di dati senza avvisi!
From e Into
I trait From e Into per conversioni sicure e infallibili:
fn main() {
// From - conversione esplicita
let s = String::from("ciao");
let n: i64 = i64::from(42_i32);
// Into - conversione inversa (implementata automaticamente)
let s: String = "ciao".into();
let n: f64 = 42_i32.into();
}
Implementare From per Tipi Personalizzati
struct Celsius(f64);
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self {
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
}
}
fn main() {
let c = Celsius(100.0);
let f = Fahrenheit::from(c);
// oppure
let c2 = Celsius(0.0);
let f2: Fahrenheit = c2.into(); // Into è automatico se From è implementato
}
TryFrom e TryInto
Per conversioni che possono fallire:
use std::convert::TryFrom;
fn main() {
// i32 → u8 potrebbe fallire
let grande: i32 = 300;
match u8::try_from(grande) {
Ok(n) => println!("Convertito: {}", n),
Err(e) => println!("Errore: {}", e), // Questo caso
}
let piccolo: i32 = 42;
let n: u8 = u8::try_from(piccolo).unwrap(); // OK: 42
// Con TryInto
let risultato: Result<u8, _> = grande.try_into();
}
ToString e Display
Conversione in stringa tramite il trait Display:
use std::fmt;
struct Punto {
x: f64,
y: f64,
}
impl fmt::Display for Punto {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Punto { x: 3.0, y: 4.0 };
let s = p.to_string(); // "(3, 4)" - grazie a Display
let n = 42.to_string(); // "42"
}
FromStr e parse
Conversione da stringa a un tipo:
fn main() {
let n: i32 = "42".parse().unwrap();
let f: f64 = "3.14".parse().unwrap();
let b: bool = "true".parse().unwrap();
// Gestione errore
match "abc".parse::<i32>() {
Ok(n) => println!("{}", n),
Err(e) => println!("Non è un numero: {}", e),
}
}
Implementare FromStr
use std::str::FromStr;
struct Colore { r: u8, g: u8, b: u8 }
impl FromStr for Colore {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parti: Vec<u8> = s.split(',')
.map(|p| p.trim().parse().map_err(|_| "Numero non valido".to_string()))
.collect::<Result<Vec<_>, _>>()?;
if parti.len() != 3 {
return Err("Servono 3 valori".to_string());
}
Ok(Colore { r: parti[0], g: parti[1], b: parti[2] })
}
}
fn main() {
let c: Colore = "255, 128, 0".parse().unwrap();
}
Conclusione
Rust offre un sistema di conversione ricco e sicuro. Usa as per casting primitivi veloci, From/Into per conversioni infallibili, TryFrom/TryInto per conversioni fallibili, e FromStr/parse per convertire da stringhe. Implementa sempre From (non Into) per i tuoi tipi: Into viene derivato automaticamente.