Salta ai contenuti

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.

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}");
}
▶ Provalo tu

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
}
▶ Provalo tu

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
}
▶ Provalo tu

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
-3
4
255
0
65
a
1
▶ Provalo tu

Qualche 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.0 diventa 255 in un u8). È diverso dagli interi, che fanno il giro (300 è diventato 44 più sopra).
  • I caratteri hanno un numero dietro (il loro code point Unicode). 'A' as u32 dà 65. Solo un u8 si può convertire direttamente in un char; per numeri più grandi useresti char::from_u32, perché non tutti i numeri sono caratteri validi.
  • I booleani diventano 1 o 0. Nella direzione opposta non funziona: 1 as bool non compila. Usa invece un confronto come n != 0.

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
▶ Provalo tu

.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.

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 + "!");
}
▶ Provalo tu

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: 25
media: 8.33
▶ Provalo tu

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.5
▶ Provalo tu

Senza 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.

  • Rust non converte mai i tipi automaticamente: fai sempre il cast in modo esplicito.
  • Usa as per 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). Anche char e bool si possono convertire in numeri.
  • Usa .parse() per trasformare del testo in un numero, e .to_string() per la direzione opposta.

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