Médio Rust
Erro customizado com From
Enunciado
Crie um erro customizado MeuErro que represente falhas de conversão e valores negativos. Implemente From<ParseIntError> para permitir o uso de ?. Depois, escreva uma função raiz_quadrada que recebe uma string, converte para f64 e retorna a raiz quadrada; se o número for negativo, retorne Err(MeuErro::Negativo).
Requisitos
- Defina
enum MeuErro { Parse(ParseIntError), Negativo }. - Implemente
From<ParseIntError> for MeuErro. - Implemente
DisplayeDebug(derive) paraMeuErro. - A função
raiz_quadradadeve usar?e retornarResult<f64, MeuErro>.
Código inicial
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
enum MeuErro {
Parse(ParseIntError),
Negativo,
}
// TODO: implemente From<ParseIntError> para MeuErro
// TODO: implemente Display para MeuErro
fn raiz_quadrada(s: &str) -> Result<f64, MeuErro> {
// TODO: converta s para f64 usando parse()? e verifique se é negativo
}
fn main() {
println!("{:?}", raiz_quadrada("16"));
println!("{:?}", raiz_quadrada("-4"));
println!("{:?}", raiz_quadrada("abc"));
}
Saída esperada
Ok(4.0)
Err(Negativo)
Err(Parse(ParseIntError { kind: InvalidDigit }))
Ver dica
Para converter ParseIntError em MeuErro, implemente From<ParseIntError> retornando MeuErro::Parse(e). No Display, use match para formatar cada variante.
Mostrar solução
use std::fmt;
use std::num::ParseIntError;
#[derive(Debug)]
enum MeuErro {
Parse(ParseIntError),
Negativo,
}
impl From<ParseIntError> for MeuErro {
fn from(e: ParseIntError) -> Self {
MeuErro::Parse(e)
}
}
impl fmt::Display for MeuErro {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MeuErro::Parse(e) => write!(f, "erro de parse: {}", e),
MeuErro::Negativo => write!(f, "número negativo não permitido"),
}
}
}
fn raiz_quadrada(s: &str) -> Result<f64, MeuErro> {
let n: f64 = s.parse()?; // converte ParseIntError em MeuErro via From
if n < 0.0 {
return Err(MeuErro::Negativo);
}
Ok(n.sqrt())
}
fn main() {
println!("{:?}", raiz_quadrada("16"));
println!("{:?}", raiz_quadrada("-4"));
println!("{:?}", raiz_quadrada("abc"));
}
Passo a passo
- Definimos
MeuErrocom duas variantes:Parse(envolveParseIntError) eNegativo. - Implementamos
From<ParseIntError>para que?possa converter automaticamente umParseIntErroremMeuErro. - Implementamos
Displaypara exibir mensagens amigáveis;Debugé derivado. - Em
raiz_quadrada,s.parse()?tenta converter paraf64. Se falhar, o erro é convertido viaFrome retornado. - Se o número for negativo, retornamos
Err(MeuErro::Negativo). - Caso contrário, retornamos
Ok(n.sqrt()). - No
main, imprimimos os resultados para ver os três casos.
Por que funciona
O operador ? usa a trait From para converter o erro original no tipo de erro da função. Assim, podemos ter erros de origens diferentes (parse, validação) unificados em um único tipo. Isso é fundamental para criar APIs robustas e expressivas.
Erros comuns
- Não implementar
From: o compilador reclama que?não pode converterParseIntErroremMeuErro. - Esquecer
Display: se você tentar usar?em uma função que retornaResult<_, MeuErro>masMeuErronão implementaDisplay, o erro só aparece se você tentar imprimir, mas é boa prática implementar. - Usar
parse::<i32>()e depois tentarsqrt():i32não temsqrt; usef64.
Outra forma de resolver
Usar a crate thiserror para derivar implementações automaticamente, mas como não usamos dependências externas, a implementação manual é didática.
Saída esperada
Ok(4.0)
Err(Negativo)
Err(Parse(ParseIntError { kind: InvalidDigit }))