Desafio Rust

Múltiplos erros e uso de ? em cadeia

Enunciado

Crie um sistema de validação de idade e email. Defina um enum ErroCadastro com variantes para idade inválida (IdadeInvalida), email sem @ (EmailInvalido) e erro de conversão (Parse). Implemente From<ParseIntError>. Escreva uma função cadastrar que recebe idade como string e email como string, converte a idade, valida se é >= 18 e se o email contém @. Use ? para propagar todos os erros. Retorne Ok(()) se tudo estiver correto.

Requisitos

  • Defina enum ErroCadastro com as três variantes e implemente From<ParseIntError>.
  • Implemente Display para ErroCadastro.
  • A função cadastrar deve usar ? pelo menos uma vez e retornar Result<(), ErroCadastro>.
  • Valide idade >= 18 e email contendo @.

Código inicial

use std::fmt;
use std::num::ParseIntError;

#[derive(Debug)]
enum ErroCadastro {
    Parse(ParseIntError),
    IdadeInvalida,
    EmailInvalido,
}

// TODO: implemente From<ParseIntError> e Display

fn cadastrar(idade: &str, email: &str) -> Result<(), ErroCadastro> {
    // TODO: converta idade com ?, valide idade >= 18, valide email contendo @
}

fn main() {
    println!("{:?}", cadastrar("25", "user@example.com"));
    println!("{:?}", cadastrar("17", "user@example.com"));
    println!("{:?}", cadastrar("abc", "user@example.com"));
    println!("{:?}", cadastrar("25", "userexample.com"));
}

Saída esperada

Ok(())
Err(IdadeInvalida)
Err(Parse(ParseIntError { kind: InvalidDigit }))
Err(EmailInvalido)
Ver dica

Use let idade: u32 = idade.parse()?; e depois verifique as condições com if. Retorne Err(ErroCadastro::IdadeInvalida) se for menor que 18.

Mostrar solução
use std::fmt;
use std::num::ParseIntError;

#[derive(Debug)]
enum ErroCadastro {
    Parse(ParseIntError),
    IdadeInvalida,
    EmailInvalido,
}

impl From<ParseIntError> for ErroCadastro {
    fn from(e: ParseIntError) -> Self {
        ErroCadastro::Parse(e)
    }
}

impl fmt::Display for ErroCadastro {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ErroCadastro::Parse(e) => write!(f, "erro de parse: {}", e),
            ErroCadastro::IdadeInvalida => write!(f, "idade deve ser maior ou igual a 18"),
            ErroCadastro::EmailInvalido => write!(f, "email deve conter @"),
        }
    }
}

fn cadastrar(idade: &str, email: &str) -> Result<(), ErroCadastro> {
    let idade: u32 = idade.parse()?; // converte e propaga erro de parse
    if idade < 18 {
        return Err(ErroCadastro::IdadeInvalida);
    }
    if !email.contains('@') {
        return Err(ErroCadastro::EmailInvalido);
    }
    Ok(())
}

fn main() {
    println!("{:?}", cadastrar("25", "user@example.com"));
    println!("{:?}", cadastrar("17", "user@example.com"));
    println!("{:?}", cadastrar("abc", "user@example.com"));
    println!("{:?}", cadastrar("25", "userexample.com"));
}

Passo a passo

  1. Definimos ErroCadastro com três variantes: Parse (para erro de conversão), IdadeInvalida e EmailInvalido.
  2. Implementamos From<ParseIntError> para permitir que ? converta automaticamente o erro de parse.
  3. Implementamos Display para mensagens legíveis.
  4. Em cadastrar, idade.parse()? converte a string para u32 e propaga erro de parse se falhar.
  5. Verificamos se a idade é menor que 18; se for, retornamos Err(IdadeInvalida).
  6. Verificamos se o email contém @; se não, retornamos Err(EmailInvalido).
  7. Se tudo passar, retornamos Ok(()).
  8. No main, testamos os quatro cenários.

Por que funciona

O uso de ? em cadeia permite que cada etapa valide e propague erros de forma limpa. A conversão From unifica o erro de parse no tipo ErroCadastro, mantendo a assinatura da função simples e expressiva. Isso demonstra como compor validações sem aninhar match.

Erros comuns

  • Esquecer de implementar From: o compilador acusa que ? não pode converter ParseIntError em ErroCadastro.
  • Usar return Err(...) dentro de um bloco if sem return: se esquecer o return, o código continua e pode retornar Ok incorretamente.
  • Comparar idade com 18 sem converter para u32: se idade for &str, a comparação não funciona; é preciso fazer o parse antes.
  • Não validar a ordem: se validar o email antes da idade, o erro reportado pode não ser o esperado; a ordem define qual erro prevalece.

Outra forma de resolver

Usar if idade < 18 { return Err(...) } else if !email.contains('@') { return Err(...) } em vez de dois if separados. É equivalente, mas a versão com dois if é mais clara para validações independentes.

Saída esperada

Ok(())
Err(IdadeInvalida)
Err(Parse(ParseIntError { kind: InvalidDigit }))
Err(EmailInvalido)