Introdução: por que o Rust não permite simplesmente converter uma string em número?
Se você veio do Python ou JavaScript, está acostumado com a mágica do tipo int("42") ou parseInt("42"). No Rust, esse truque não funciona — o compilador exigirá que você especifique claramente em qual tipo deseja transformar a string. Por quê? Porque Rust é uma linguagem que não perdoa descuidos. Ela força você a pensar em casos extremos: e se a string não contiver um número? E se o número for grande demais para o tipo escolhido?
Neste artigo, vamos abordar a pergunta mais popular no Stack Overflow entre iniciantes em Rust: como converter String para int. Você não terá apenas um código pronto, mas também uma compreensão completa do que acontece nos bastidores.
Método parse() — a principal ferramenta de conversão
A principal forma de converter uma string em número em Rust é o método parse(), que está disponível para os tipos String e &str. Ele é implementado através da trait FromStr e retorna um Result<T, E>.
Exemplo básico: String → i32
fn main() { let number_string = String::from("42"); let number: i32 = number_string.parse().unwrap(); println!("Número: {}", number); // Saída: Número: 42}Observe o unwrap(). Esta é uma forma rápida de "extrair" o valor de um Result, mas é perigosa: se a string não for um número, o programa entrará em pânico e travará.
Tratamento de erros correto
Em código real, você nunca deve usar unwrap() sem pensar. Veja como fazer corretamente:
use std::num::ParseIntError;
fn main() { let input = String::from("hello"); match input.parse::<i32>() { Ok(number) => println!("Sucesso: {}", number), Err(e) => println!("Erro de conversão: {}", e), }}Ou uma versão mais concisa com if let:
fn main() { let input = String::from("2024"); if let Ok(year) = input.parse::<i32>() { println!("Ano: {}", year); } else { println!("Não foi possível fazer o parse da string"); }}Tipos inteiros disponíveis para conversão
Rust oferece uma rica variedade de tipos inteiros. Você pode converter uma string para qualquer um deles:
i8,i16,i32,i64,i128— inteiros com sinalu8,u16,u32,u64,u128— inteiros sem sinalisize,usize— tamanho depende da arquitetura
Exemplo de conversão para diferentes tipos:
fn main() { let val = "255"; let a: u8 = val.parse().unwrap(); // 255 let b: i16 = val.parse().unwrap(); // 255 let c: u32 = val.parse().unwrap(); // 255 // Isso aqui daria erro — 255 não cabe em i8 // let d: i8 = val.parse().unwrap(); // PANIC!}Técnicas avançadas: from_str e tipos personalizados
O método parse() internamente chama from_str() da trait FromStr. Você pode implementar essa trait para suas próprias estruturas:
use std::str::FromStr;use std::num::ParseIntError;
#[derive(Debug)]struct Version { major: u32, minor: u32,}
impl FromStr for Version { type Err = ParseIntError; fn from_str(s: &str) -> Result<Self, Self::Err> { let parts: Vec<&str> = s.split('.').collect(); if parts.len() != 2 { // Criando nosso próprio erro, mas para exemplo usamos ParseIntError return Err("1.0".parse::<u32>().unwrap_err()); } let major = parts[0].p