Visao Geral do Modulo std::convert
O modulo std::convert define as traits padrao de conversao do Rust. Essas traits formam o contrato universal para transformar valores de um tipo em outro, de forma segura e idiomatica.
As quatro traits principais formam dois pares:
From<T>/Into<T>— conversoes infaliveis (que nunca falham)TryFrom<T>/TryInto<T>— conversoes faliveis (que podem retornar erro)
Alem dessas, o modulo inclui:
AsRef<T>/AsMut<T>— conversoes baratas por referenciaInfallible— tipo de erro impossivel (usado comFrom)identity— funcao identidade (retorna o proprio valor)
Essas traits sao tao fundamentais que a maioria esta incluida no prelude, ou seja, voce nao precisa importa-las explicitamente.
Traits e Funcoes Principais
From e Into
| Trait | Metodo | Descricao |
|---|---|---|
From<T> | fn from(T) -> Self | Converte de T para Self |
Into<T> | fn into(self) -> T | Converte self para T |
Regra: Implemente From<T>. A implementacao de Into<T> e gerada automaticamente pela blanket implementation. Nunca implemente Into diretamente.
TryFrom e TryInto
| Trait | Metodo | Descricao |
|---|---|---|
TryFrom<T> | fn try_from(T) -> Result<Self, Error> | Conversao falivel |
TryInto<T> | fn try_into(self) -> Result<T, Error> | Conversao falivel |
AsRef e AsMut
| Trait | Metodo | Descricao |
|---|---|---|
AsRef<T> | fn as_ref(&self) -> &T | Referencia barata |
AsMut<T> | fn as_mut(&mut self) -> &mut T | Referencia mutavel barata |
Outros
| Item | Descricao |
|---|---|
Infallible | Tipo de erro que nunca pode ser construido |
identity(x) | Retorna x sem modificacao |
Exemplos Praticos
1. Implementando From para Tipos Customizados
#[derive(Debug)]
struct Email {
endereco: String,
}
// Conversao de &str para Email
impl From<&str> for Email {
fn from(s: &str) -> Self {
Email {
endereco: s.to_lowercase(),
}
}
}
// Conversao de String para Email
impl From<String> for Email {
fn from(s: String) -> Self {
Email {
endereco: s.to_lowercase(),
}
}
}
#[derive(Debug)]
struct Celsius(f64);
#[derive(Debug)]
struct Fahrenheit(f64);
impl From<Celsius> for Fahrenheit {
fn from(c: Celsius) -> Self {
Fahrenheit(c.0 * 9.0 / 5.0 + 32.0)
}
}
impl From<Fahrenheit> for Celsius {
fn from(f: Fahrenheit) -> Self {
Celsius((f.0 - 32.0) * 5.0 / 9.0)
}
}
fn main() {
// From explicito
let email = Email::from("Usuario@Email.COM");
println!("{email:?}"); // Email { endereco: "usuario@email.com" }
// Into implicito (gerado automaticamente pelo From)
let email2: Email = "outro@teste.com".into();
println!("{email2:?}");
// Conversao de temperatura
let agua_fervendo = Celsius(100.0);
let em_fahrenheit: Fahrenheit = agua_fervendo.into();
println!("{em_fahrenheit:?}"); // Fahrenheit(212.0)
let corpo = Fahrenheit(98.6);
let em_celsius: Celsius = corpo.into();
println!("{em_celsius:?}"); // Celsius(37.0)
}
2. TryFrom para Conversoes Faliveis
use std::num::TryFromIntError;
#[derive(Debug)]
struct Idade(u8);
#[derive(Debug)]
enum IdadeErro {
MuitoJovem,
MuitoVelho,
NumeroInvalido(TryFromIntError),
}
impl std::fmt::Display for IdadeErro {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IdadeErro::MuitoJovem => write!(f, "Idade deve ser pelo menos 0"),
IdadeErro::MuitoVelho => write!(f, "Idade deve ser no maximo 150"),
IdadeErro::NumeroInvalido(e) => write!(f, "Numero invalido: {e}"),
}
}
}
impl TryFrom<i32> for Idade {
type Error = IdadeErro;
fn try_from(valor: i32) -> Result<Self, Self::Error> {
if valor < 0 {
Err(IdadeErro::MuitoJovem)
} else if valor > 150 {
Err(IdadeErro::MuitoVelho)
} else {
// u8::try_from(i32) pode falhar se i32 > 255,
// mas ja validamos que esta entre 0 e 150
Ok(Idade(valor as u8))
}
}
}
fn main() {
let idades = [25, -5, 200, 0, 100];
for &valor in &idades {
match Idade::try_from(valor) {
Ok(idade) => println!("{valor} -> {idade:?}"),
Err(e) => println!("{valor} -> Erro: {e}"),
}
}
// TryInto tambem funciona (gerado automaticamente)
let resultado: Result<Idade, _> = 30i32.try_into();
println!("\n30.try_into() = {resultado:?}");
// Conversoes entre inteiros (TryFrom ja implementado na stdlib)
let grande: i64 = 1_000_000;
let tentativa: Result<i16, _> = grande.try_into();
println!("i64({grande}) -> i16: {tentativa:?}"); // Err(TryFromIntError)
}
3. AsRef para Funcoes Genericas
use std::path::Path;
// AsRef<str> aceita tanto &str quanto String
fn contar_palavras(texto: impl AsRef<str>) -> usize {
texto.as_ref().split_whitespace().count()
}
// AsRef<Path> aceita &str, String, PathBuf, &Path...
fn arquivo_existe(caminho: impl AsRef<Path>) -> bool {
caminho.as_ref().exists()
}
// AsRef<[u8]> aceita &[u8], Vec<u8>, String, &str...
fn calcular_hash(dados: impl AsRef<[u8]>) -> u64 {
let bytes = dados.as_ref();
// Hash simples para exemplo
bytes.iter().fold(0u64, |acc, &b| {
acc.wrapping_mul(31).wrapping_add(b as u64)
})
}
fn main() {
// contar_palavras aceita diferentes tipos de string
println!("{}", contar_palavras("Rust e incrivel")); // 3
println!("{}", contar_palavras(String::from("Ola mundo"))); // 2
// arquivo_existe aceita diferentes tipos de caminho
println!("{}", arquivo_existe("/tmp"));
println!("{}", arquivo_existe(String::from("/nao/existe")));
// calcular_hash aceita bytes de diferentes fontes
println!("{}", calcular_hash("texto"));
println!("{}", calcular_hash(vec![1u8, 2, 3]));
println!("{}", calcular_hash(b"bytes" as &[u8]));
}
4. From para Conversao de Erros
Um dos usos mais poderosos de From e a conversao automatica de erros com o operador ?:
use std::io;
use std::num::ParseIntError;
#[derive(Debug)]
enum AppErro {
Io(io::Error),
Parse(ParseIntError),
Validacao(String),
}
// Implementando From para cada tipo de erro
impl From<io::Error> for AppErro {
fn from(e: io::Error) -> Self {
AppErro::Io(e)
}
}
impl From<ParseIntError> for AppErro {
fn from(e: ParseIntError) -> Self {
AppErro::Parse(e)
}
}
impl std::fmt::Display for AppErro {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppErro::Io(e) => write!(f, "Erro de I/O: {e}"),
AppErro::Parse(e) => write!(f, "Erro de parse: {e}"),
AppErro::Validacao(msg) => write!(f, "Validacao: {msg}"),
}
}
}
// Agora o operador ? converte automaticamente os erros!
fn processar_arquivo(caminho: &str) -> Result<i32, AppErro> {
let conteudo = std::fs::read_to_string(caminho)?; // io::Error -> AppErro
let numero: i32 = conteudo.trim().parse()?; // ParseIntError -> AppErro
if numero < 0 {
return Err(AppErro::Validacao("Numero deve ser positivo".into()));
}
Ok(numero * 2)
}
fn main() {
match processar_arquivo("/tmp/numero.txt") {
Ok(resultado) => println!("Resultado: {resultado}"),
Err(e) => println!("Erro: {e}"),
}
}
5. Padroes Avancados de Conversao
// Padrao: construtor generico com Into
struct Configuracao {
host: String,
porta: u16,
nome: String,
}
impl Configuracao {
// Aceita qualquer tipo que pode ser convertido em String
fn new(host: impl Into<String>, porta: u16, nome: impl Into<String>) -> Self {
Configuracao {
host: host.into(),
porta,
nome: nome.into(),
}
}
}
// Padrao: colecao de itens convertidos
fn criar_lista_de_strings(itens: &[&str]) -> Vec<String> {
itens.iter().map(|&s| String::from(s)).collect()
}
// Padrao: identity como funcao de primeira classe
fn processar<F>(dados: Vec<i32>, transformar: F) -> Vec<i32>
where
F: Fn(i32) -> i32,
{
dados.into_iter().map(transformar).collect()
}
fn main() {
// Construtor generico — aceita &str e String
let cfg = Configuracao::new("localhost", 8080, String::from("meu_app"));
println!("{}:{} ({})", cfg.host, cfg.porta, cfg.nome);
// identity — util com APIs que exigem uma funcao
let dados = vec![1, 2, 3];
let sem_mudanca = processar(dados.clone(), std::convert::identity);
let dobrados = processar(dados, |x| x * 2);
println!("Identidade: {sem_mudanca:?}");
println!("Dobrados: {dobrados:?}");
// Conversoes embutidas na stdlib
let n: i64 = i64::from(42i32); // i32 -> i64 (sempre seguro)
let s: String = String::from("ola"); // &str -> String
let v: Vec<u8> = Vec::from(&[1u8, 2, 3][..]); // &[u8] -> Vec<u8>
println!("{n}, {s}, {v:?}");
}
Padroes Comuns
Quando Implementar From vs TryFrom
| Situacao | Use |
|---|---|
| Conversao sempre funciona | From |
| Conversao pode falhar | TryFrom |
| Conversao de subconjunto (ex: u8 -> u32) | From |
| Conversao de superconjunto (ex: u32 -> u8) | TryFrom |
| Parsing de strings | TryFrom ou FromStr |
| Conversao de erros | From |
Cadeia de Conversao com Into em Parametros
Prefira impl Into<T> em parametros de funcao para flexibilidade:
fn saudar(nome: impl Into<String>) {
let nome = nome.into();
println!("Ola, {nome}!");
}
AsRef vs Borrow
AsRef<T>e para conversao generica de referenciaBorrow<T>e para quando o tipo emprestado tem as mesmas garantias de Eq/Hash/Ord (importante para chaves de HashMap)
Quando Usar (e Quando Nao Usar)
Use std::convert quando:
- Precisar converter entre tipos de forma idiomatica
- Quiser que o operador
?converta erros automaticamente - Quiser funcoes genericas que aceitem multiplos tipos de entrada
- Precisar de validacao na conversao (
TryFrom)
Nao use std::convert quando:
- A conversao envolve efeitos colaterais (I/O, rede) — use metodos explicitos
- A conversao e ambigua ou tem multiplas interpretacoes — use metodos nomeados
- Dois tipos tem From reciproco e isso pode causar confusao
- A conversao e custosa — documente claramente ou use um metodo explicito
Dica de performance: From/Into tem custo zero de abstracao — o compilador inline e otimiza as conversoes. AsRef tambem e custo zero, pois retorna apenas uma referencia.
Veja Tambem
- From e Into em Detalhes — aprofundamento nas traits de conversao
- Converter String para Numero — receita pratica com
parseeTryFrom - Modulo std::any — verificacao de tipos em runtime
- O Prelude do Rust — quais traits de conversao sao importadas automaticamente
- String e &str — conversoes entre tipos de texto
- Documentacao oficial — std::convert