Conversione di tipo (cast) in Rust
Rust non converte automaticamente un tipo in un altro: se mescoli un i32 e un f64 nella stessa espressione, il codice non compila. Devi convertire in modo esplicito. È voluto: le conversioni silenziose sono una fonte comune di bug in altri linguaggi.
Convertire tra tipi numerici con as
Sezione intitolata “Convertire tra tipi numerici con as”Usa la parola chiave as per fare il cast (conversione) di un tipo numerico in un altro:
fn main() { let x: i32 = 10; let y: f64 = x as f64; // i32 -> f64
println!("{y}");}Conta più di quanto sembri: prova a dividere due interi senza cast, e otterrai una divisione intera invece del risultato decimale che probabilmente volevi:
fn main() { let a = 10; let b = 3;
println!("{}", a / b); // 3 (divisione intera) println!("{}", a as f64 / b as f64); // 3.3333333333333335}Convertire verso un tipo più piccolo può perdere dati
Sezione intitolata “Convertire verso un tipo più piccolo può perdere dati”Convertire da un tipo più grande a uno più piccolo (come da i32 a u8) non fallisce: tronca e basta, e questo può darti un risultato sorprendente:
fn main() { let big: i32 = 300; let small = big as u8; // u8 contiene solo 0-255 println!("{small}"); // 44, non 300: ha fatto il giro}Convertire float, caratteri e booleani
Sezione intitolata “Convertire float, caratteri e booleani”as funziona anche con qualche tipo in più oltre ai numeri interi. Ognuno ha una regola che vale la pena conoscere:
fn main() { println!("{}", 3.99_f64 as i32); // 3: i decimali vengono tagliati println!("{}", -3.99_f64 as i32); // -3: taglia verso lo zero, non verso il basso println!("{}", 3.99_f64.round() as i32); // 4: arrotonda prima, se è quello che vuoi println!("{}", 300.0_f64 as u8); // 255: troppo grande, si ferma al massimo println!("{}", -5.0_f64 as u8); // 0: troppo piccolo, si ferma al minimo println!("{}", 'A' as u32); // 65: il numero di codice di un char println!("{}", 97_u8 as char); // a: e ritorno println!("{}", true as i32); // 1 (false sarebbe 0)}Output:
3-34255065a1Qualche cosa da ricordare:
- Da float a intero butta via tutto quello che c’è dopo la virgola. Non arrotonda. Chiama prima
.round()se vuoi l’intero più vicino. - I float fuori intervallo vengono bloccati al valore più vicino che ci sta (
300.0diventa255in unu8). È diverso dagli interi, che fanno il giro (300è diventato44più sopra). - I caratteri hanno un numero dietro (il loro code point Unicode).
'A' as u32dà65. Solo unu8si può convertire direttamente in unchar; per numeri più grandi userestichar::from_u32, perché non tutti i numeri sono caratteri validi. - I booleani diventano
1o0. Nella direzione opposta non funziona:1 as boolnon compila. Usa invece un confronto comen != 0.
Trasformare stringhe in numeri
Sezione intitolata “Trasformare stringhe in numeri”Il cast con as funziona solo tra tipi numerici. Per trasformare del testo in un numero, per esempio qualcosa scritto dall’utente, usa .parse():
fn main() { let text = "42"; let number: i32 = text.parse().expect("non è un numero valido");
println!("{}", number + 8);}Output:
50.parse() può fallire (e se il testo non fosse affatto un numero?), quindi restituisce un Result invece del numero direttamente. Non abbiamo ancora visto Result: per ora, .expect("messaggio") è un modo semplice per dire «se fallisce, vai in crash con questo messaggio». Vedrai il modo corretto di gestirlo nella pagina Result.
Da numeri a stringhe
Sezione intitolata “Da numeri a stringhe”Nella direzione opposta è più semplice: quasi ogni tipo può diventare una String con .to_string():
fn main() { let number = 42; let text = number.to_string(); println!("{}", text + "!");}Pratica
Sezione intitolata “Pratica”1. Punteggio medio
Sezione intitolata “1. Punteggio medio”Salva tre voti come valori i32: 7, 8 e 10. Stampa la loro somma, poi la media con due cifre decimali. Suggerimento: se dividi due interi ottieni 8, non 8.33, quindi converti prima la somma in f64. {:.2} stampa due decimali.
Mostra la soluzione
fn main() { let a: i32 = 7; let b: i32 = 8; let c: i32 = 10;
let sum = a + b + c; let average = sum as f64 / 3.0; // 25.0 / 3.0, non 25 / 3
println!("somma: {sum}"); println!("media: {:.2}", average);}Output:
somma: 25media: 8.332. Un prezzo scritto come testo
Sezione intitolata “2. Un prezzo scritto come testo”Un negozio ti dà il prezzo come testo, "4.50", e la quantità come i32, 3. Trasforma il testo in un f64, moltiplicalo per la quantità e stampa Totale: 13.5. Suggerimento: ti servono entrambi gli strumenti di questa pagina: .parse() per il testo e as f64 per la quantità.
Mostra la soluzione
fn main() { let price_text = "4.50"; let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("non è un prezzo valido"); let total = price * quantity as f64; // f64 * f64
println!("Totale: {total}");}Output:
Totale: 13.5Senza as f64, Rust si rifiuterebbe di moltiplicare un f64 per un i32, perché non mescola mai i tipi numerici al posto tuo. Nota anche che .parse() vuole il punto come separatore decimale: "4,50" fallirebbe.
Riepilogo
Sezione intitolata “Riepilogo”- Rust non converte mai i tipi automaticamente: fai sempre il cast in modo esplicito.
- Usa
asper convertire tra tipi numerici:x as f64,x as u8. - Convertire verso un tipo più piccolo tronca in silenzio: non dà errore, quindi fai attenzione.
- Da float a intero taglia i decimali (usa prima
.round()per arrotondare). Anchechareboolsi possono convertire in numeri. - Usa
.parse()per trasformare del testo in un numero, e.to_string()per la direzione opposta.
Verifica veloce
Sezione intitolata “Verifica veloce”Verifica veloce
1. Cosa succede quando dividi due interi come 10 / 3 in Rust senza cast?
2. Cosa succede quando converti 300_i32 as u8?
3. Cosa restituisce .parse() quando converte una stringa in un numero, visto che la conversione può fallire?
Punteggio: 0 / 3
Per approfondire
Sezione intitolata “Per approfondire”- Casting — Rust by Example (in inglese): tanti cast con
aseseguibili, con commenti su cosa produce esattamente ciascuno. - Type cast expressions — The Rust Reference (in inglese): la tabella completa dei cast permessi e la regola precisa per ciascuno.
- To and from Strings — Rust by Example (in inglese):
.to_string()e.parse()un po’ più a fondo. TryFrom— documentazione della libreria standard (in inglese): la conversione controllata (u8::try_from(...)) da usare quando un valore potrebbe non starci.- Numeric Types — The Rust Reference (in inglese): dimensione e intervallo di ogni tipo intero e float.