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 ErroCadastrocom as três variantes e implementeFrom<ParseIntError>. - Implemente
DisplayparaErroCadastro. - A função
cadastrardeve usar?pelo menos uma vez e retornarResult<(), 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
- Definimos
ErroCadastrocom três variantes:Parse(para erro de conversão),IdadeInvalidaeEmailInvalido. - Implementamos
From<ParseIntError>para permitir que?converta automaticamente o erro de parse. - Implementamos
Displaypara mensagens legíveis. - Em
cadastrar,idade.parse()?converte a string parau32e propaga erro de parse se falhar. - Verificamos se a idade é menor que 18; se for, retornamos
Err(IdadeInvalida). - Verificamos se o email contém
@; se não, retornamosErr(EmailInvalido). - Se tudo passar, retornamos
Ok(()). - 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 converterParseIntErroremErroCadastro. - Usar
return Err(...)dentro de um blocoifsemreturn: se esquecer oreturn, o código continua e pode retornarOkincorretamente. - Comparar idade com
18sem converter parau32: seidadefor&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)