Percursos
/
Cairo
Cairo
/
Programa
/
Tratamento de Erros
Tr

Tratamento de Erros em Cairo

1 exercício

Sobre Tratamento de Erros

O Cairo disponibiliza técnicas de tratamento de erros para resolver problemas e gerir erros, permitindo que o código corra sem problemas durante a execução.

Existem dois tipos de erros em Cairo:

  • Erros irrecuperáveis com panic - estes erros terminam o programa imediatamente.
  • Erros recuperáveis com Result - estes erros podem ser tratados em tempo de execução.

Erros irrecuperáveis com panic

Em Cairo, podem surgir problemas inesperados durante a execução do programa, originando erros de execução a que se chama "panics". Isto interrompe a execução do programa, o que é útil quando não faz sentido continuar. Um panic pode ocorrer de forma inadvertida, através de ações que fazem o código entrar em panic (por exemplo, aceder a um array para lá dos seus limites), ou de forma deliberada, invocando a função panic.

fn main() {
    let data = array![1, 2];

    if true {
        panic(data);
    }
    println!("This line will not be reached.");
}

Executar este exemplo imprimiria o seguinte na consola:

Run panicked with [1, 2, ].

Existe também uma macro especial, chamada panic!, que permite usar um ByteArray como mensagem de erro.

panic!("The error for the panic! Error message is not limited to 31 characters anymore");

A macro assert!

A macro assert! é uma ferramenta útil para impor condições específicas no teu código. Se a condição em assert! for avaliada como false, o programa entra em panic com uma mensagem de erro do tipo ByteArray. Isto é usado frequentemente para verificar suposições durante o desenvolvimento e garantir que os valores cumprem determinados critérios.

Por exemplo:

fn main() {
    let x = 5;
    assert!(x > 0, "x must be greater than zero");
}

Se x não for maior do que zero, o programa entra em panic com a mensagem "x must be greater than zero". O assert! é útil para verificar invariantes e pré-condições sem escreveres manualmente código de tratamento de erros.

Notação nopanic

A notação nopanic de Cairo indica que uma função nunca entra em panic.

fn function_never_panic() -> felt252 nopanic {
    108
}

Uma função nopanic só pode ser chamada numa função também anotada como nopanic.

Atributo panic_with

O atributo panic_with de Cairo serve para marcar uma função que devolve um Option ou um Result e cria um wrapper para a função anotada, que entra em panic se a função devolver Option::None ou Result::Err.

#[panic_with('value is 0', wrap_not_zero)]
fn check_not_zero(value: u128) -> Option<u128> {
    if value == 0 {
        Option::None
    } else {
        Option::Some(value)
    }
}

fn main() {
    check_not_zero(0); // -> returns Option::None
    wrap_not_zero(0);  // -> panics with 'value is 0'
}

Erros recuperáveis com Result

O enum Result trata os erros de forma controlada, fornecendo feedback em vez de provocar uma falha ou um comportamento indefinido.

enum Result<T, E> {
    Ok: T,
    Err: E,
}

Vamos ver um exemplo de como podemos usar este enum para tratar uma divisão por zero:

fn divide(a: u32, b: u32) -> Result<u32, ByteArray> {
    if b == 0 {
        return Result::Err("Error: Division by zero"); // Return an error if division by zero is attempted
    }
    Result::Ok(a / b)
}

fn main() {
    // Test with division by zero
    match divide(10, 0) {
        Result::Ok(result) => println!("Division successful: {}", result),
        Result::Err(e) => println!("{}", e),
    }
}

O operador ?

O operador ? serve para um tratamento de erros conciso e implícito, deixando que a função que chama lide com os erros que possam ocorrer. Quando usas o operador ? num tipo Result ou Option, acontece o seguinte:

  • Se o valor for Result::Ok(x) ou Option::Some(x), devolve diretamente o valor interno x.
  • Se o valor for Result::Err(e) ou Option::None, propaga o erro ou o None, regressando imediatamente da função.
fn parse_u8(s: felt252) -> Result<u8, felt252> {
    match s.try_into() {
        Option::Some(value) => Result::Ok(value),
        Option::None => Result::Err('Invalid integer'),
    }
}

fn do_something_with_parse_u8(input: felt252) -> Result<u8, felt252> {
    let input_to_u8: u8 = parse_u8(input)?;
    // DO SOMETHING
    let res = input_to_u8 - 1;
    Result::Ok(res)
}

fn main() {
    let number: felt252 = 258;
    match do_something_with_parse_u8(number) {
        Result::Ok(value) => println!("Result: {}", value),
        Result::Err(e) => println!("Error: {}", e),
    }
}
Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Tratamento de Erros

A prática está bloqueada

Desbloqueia mais 1 exercício para praticares Tratamento de Erros