Desafio Rust

Testando erros e pânicos

Enunciado

Implemente uma função raiz_quadrada(x: f64) -> Result<f64, String> que retorna Err para números negativos, e uma função divide(a: i32, b: i32) -> i32 que entra em pânico se b == 0. Escreva testes que cubram: sucesso da raiz, erro da raiz, e pânico na divisão usando #[should_panic].

Requisitos

  • raiz_quadrada retorna Ok para x >= 0 e Err para x < 0.
  • divide deve entrar em pânico se b == 0.
  • Os testes devem usar assert_eq! para raiz_quadrada e #[should_panic] para divide.
  • O teste de pânico deve verificar a mensagem de erro com expected.

Código inicial

pub fn raiz_quadrada(x: f64) -> Result<f64, String> {
    // implemente
}

pub fn divide(a: i32, b: i32) -> i32 {
    // implemente
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn testa_raiz_ok() {
        // escreva o teste
    }

    #[test]
    fn testa_raiz_negativa() {
        // escreva o teste
    }

    #[test]
    #[should_panic(expected = "divisão por zero")]
    fn testa_divisao_por_zero() {
        // escreva o teste
    }
}

fn main() {
    println!("{:?}", raiz_quadrada(9.0));
    println!("{:?}", raiz_quadrada(-1.0));
    println!("{}", divide(10, 2));
}

Saída esperada

Ok(3.0)
Err("número negativo")
5
Ver dica

Para raiz_quadrada, use if x < 0.0 { Err(...) } else { Ok(x.sqrt()) }. Para divide, use assert!(b != 0, "divisão por zero") ou panic!.

Mostrar solução
pub fn raiz_quadrada(x: f64) -> Result<f64, String> {
    if x < 0.0 {
        Err("número negativo".to_string()) // retorna erro para negativos
    } else {
        Ok(x.sqrt()) // calcula a raiz quadrada
    }
}

pub fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("divisão por zero"); // entra em pânico se divisor for zero
    }
    a / b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn testa_raiz_ok() {
        assert_eq!(raiz_quadrada(9.0), Ok(3.0)); // 9.0 -> Ok(3.0)
    }

    #[test]
    fn testa_raiz_negativa() {
        assert_eq!(raiz_quadrada(-1.0), Err("número negativo".to_string()));
    }

    #[test]
    #[should_panic(expected = "divisão por zero")]
    fn testa_divisao_por_zero() {
        divide(10, 0); // deve entrar em pânico
    }
}

fn main() {
    println!("{:?}", raiz_quadrada(9.0));
    println!("{:?}", raiz_quadrada(-1.0));
    println!("{}", divide(10, 2));
}

Passo a passo

  1. raiz_quadrada verifica se x é negativo; se for, retorna Err com mensagem.
  2. Caso contrário, retorna Ok(x.sqrt()).
  3. divide verifica se b == 0 e chama panic! com a mensagem "divisão por zero".
  4. No teste testa_raiz_ok, usamos assert_eq! para comparar Ok(3.0) com o resultado.
  5. No teste testa_raiz_negativa, comparamos com Err("número negativo".to_string()).
  6. O teste testa_divisao_por_zero é anotado com #[should_panic(expected = "divisão por zero")], que verifica se a mensagem do pânico contém o texto esperado.
  7. Se divide(10, 0) não entrar em pânico, o teste falha.

Por que funciona

Result permite tratar erros de forma explícita, e os testes comparam o valor retornado com o esperado. Já #[should_panic] captura pânicos e verifica se ocorreram, opcionalmente checando a mensagem. Isso cobre tanto erros recuperáveis quanto irrecoveráveis.

Erros comuns

  • Esquecer expected em #[should_panic]: o teste passa mesmo que o pânico tenha mensagem diferente.
  • Usar assert_eq! para testar pânico: o pânico interrompe o teste antes da asserção.
  • Retornar Err em vez de panic! em divide: o teste should_panic falharia, pois não há pânico.

Outra forma de resolver

Poderia usar std::panic::catch_unwind para capturar o pânico e verificar a mensagem manualmente, mas #[should_panic] é mais idiomático e simples.

Saída esperada

Ok(3.0)
Err("número negativo")
5