Pular para o conteúdo

Conversão de tipos em Rust

O Rust não converte um tipo em outro automaticamente para você: se você misturar um i32 e um f64 na mesma expressão, o código não compila. A conversão precisa ser explícita. Isso é de propósito, porque conversões silenciosas são uma fonte comum de bugs em outras linguagens.

Use a palavra-chave as para converter (fazer um cast de) um tipo numérico em outro:

fn main() {
let x: i32 = 10;
let y: f64 = x as f64; // i32 -> f64
println!("{y}");
}
▶ Experimente você mesmo

Isso importa mais do que parece. Experimente dividir dois inteiros sem converter e você vai ter uma divisão inteira, em vez do resultado decimal que provavelmente queria:

fn main() {
let a = 10;
let b = 3;
println!("{}", a / b); // 3 (divisão inteira)
println!("{}", a as f64 / b as f64); // 3.3333333333333335
}
▶ Experimente você mesmo

Converter de um tipo maior para um menor (como de i32 para u8) não dá erro: o valor simplesmente é cortado, o que pode gerar um resultado surpreendente:

fn main() {
let big: i32 = 300;
let small = big as u8; // u8 só guarda de 0 a 255
println!("{small}"); // 44, e não 300: o valor deu a volta
}
▶ Experimente você mesmo

O as funciona com mais alguns tipos além dos números inteiros. Cada um tem uma regra que vale a pena conhecer:

fn main() {
println!("{}", 3.99_f64 as i32); // 3: as casas decimais são cortadas
println!("{}", -3.99_f64 as i32); // -3: corta em direção ao zero, não para baixo
println!("{}", 3.99_f64.round() as i32); // 4: arredonde antes, se é isso que você quer
println!("{}", 300.0_f64 as u8); // 255: grande demais, então para no máximo
println!("{}", -5.0_f64 as u8); // 0: pequeno demais, então para no mínimo
println!("{}", 'A' as u32); // 65: o número de código de um char
println!("{}", 97_u8 as char); // a: e de volta
println!("{}", true as i32); // 1 (false seria 0)
}

Saída:

3
-3
4
255
0
65
a
1
▶ Experimente você mesmo

O que levar daqui:

  • De float para inteiro, tudo depois do ponto decimal é jogado fora. Não há arredondamento. Chame .round() antes se quiser o número inteiro mais próximo.
  • Floats fora da faixa são limitados ao valor mais próximo que cabe (300.0 vira 255 em um u8). Isso é diferente dos inteiros, que dão a volta (300 virou 44 lá em cima).
  • Caracteres têm um número por trás (o code point Unicode deles). 'A' as u32 dá 65. Só um u8 pode ser convertido direto de volta para char; para números maiores você usaria char::from_u32, porque nem todo número é um caractere válido.
  • Booleanos viram 1 ou 0. O caminho inverso não funciona: 1 as bool não compila. Use uma comparação como n != 0 no lugar.

A conversão com as só funciona entre tipos numéricos. Para transformar um texto em número (por exemplo, algo que o usuário digitou), use .parse():

fn main() {
let text = "42";
let number: i32 = text.parse().expect("não é um número válido");
println!("{}", number + 8);
}

Saída:

50
▶ Experimente você mesmo

O .parse() pode falhar (e se o texto nem for um número?), por isso ele retorna um Result em vez do número diretamente. Ainda não vimos o Result. Por enquanto, .expect("mensagem") é um jeito simples de dizer “se der errado, pare o programa com esta mensagem”. Você vai ver o jeito certo de tratar isso na página Result.

O caminho inverso é mais simples: quase todo tipo pode virar uma String com .to_string():

fn main() {
let number = 42;
let text = number.to_string();
println!("{}", text + "!");
}
▶ Experimente você mesmo

Guarde três notas de prova como valores i32: 7, 8 e 10. Imprima a soma e depois a média com duas casas decimais. Dica: se você dividir dois inteiros, vai obter 8, e não 8.33, então converta a soma para f64 antes. {:.2} imprime duas casas decimais.

Ver solução
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, e não 25 / 3
println!("soma: {sum}");
println!("média: {:.2}", average);
}

Saída:

soma: 25
média: 8.33
▶ Experimente você mesmo

Uma loja passa o preço como texto, "4.50", e a quantidade como um i32, 3. Transforme o texto em f64, multiplique pela quantidade e imprima Total: 13.5. Dica: você precisa das duas ferramentas desta página: .parse() para o texto e as f64 para a quantidade.

Ver solução
fn main() {
let price_text = "4.50";
let quantity: i32 = 3;
let price: f64 = price_text.parse().expect("não é um preço válido");
let total = price * quantity as f64; // f64 * f64
println!("Total: {total}");
}

Saída:

Total: 13.5
▶ Experimente você mesmo

Sem o as f64, o Rust se recusaria a multiplicar um f64 por um i32, porque ele nunca mistura tipos numéricos por você. Repare também que o .parse() espera um ponto como separador decimal: "4,50" não seria um número válido.

  • O Rust nunca converte tipos automaticamente: você sempre faz a conversão de forma explícita.
  • Use as para converter entre tipos numéricos: x as f64, x as u8.
  • Converter para um tipo menor corta o valor em silêncio, sem dar erro, então tome cuidado.
  • De float para inteiro, as casas decimais são cortadas (use .round() antes para arredondar). char e bool também podem ser convertidos em números.
  • Use .parse() para transformar texto em número e .to_string() para fazer o contrário.

Teste o que aprendeu

1. O que acontece quando você divide dois inteiros, como 10 / 3, em Rust sem converter?

2. O que acontece quando você converte 300_i32 com as u8?

3. O que o .parse() retorna ao converter uma string em número, já que a conversão pode falhar?

Pontuação: 0 / 3