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 Display e Debug (derive) para MeuErro.
  • A função raiz_quadrada deve usar ? e retornar Result<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

  1. Definimos MeuErro com duas variantes: Parse (envolve ParseIntError) e Negativo.
  2. Implementamos From<ParseIntError> para que ? possa converter automaticamente um ParseIntError em MeuErro.
  3. Implementamos Display para exibir mensagens amigáveis; Debug é derivado.
  4. Em raiz_quadrada, s.parse()? tenta converter para f64. Se falhar, o erro é convertido via From e retornado.
  5. Se o número for negativo, retornamos Err(MeuErro::Negativo).
  6. Caso contrário, retornamos Ok(n.sqrt()).
  7. 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 converter ParseIntError em MeuErro.
  • Esquecer Display: se você tentar usar ? em uma função que retorna Result<_, MeuErro> mas MeuErro não implementa Display, o erro só aparece se você tentar imprimir, mas é boa prática implementar.
  • Usar parse::<i32>() e depois tentar sqrt(): i32 não tem sqrt; use f64.

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 }))