Trilhas
/
Cairo
Cairo
/
Programa
/
Tratamento de erros
Tr

Tratamento de erros em Cairo

1 exercício

Sobre Tratamento de erros

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

Existem dois tipos de erros em Cairo:

  • Erros irrecuperáveis com panic: esses erros encerram o programa imediatamente.
  • Erros recuperáveis com Result: esses erros podem ser tratados em tempo de execução.

Erros irrecuperáveis com panic

Em Cairo, problemas inesperados podem surgir durante a execução do programa, gerando erros de tempo de execução chamados de "panics". Isso interrompe a execução do programa, o que é útil quando continuar não faria sentido. Um panic pode ocorrer de forma inadvertida, por meio de ações que fazem o código entrar em panic (por exemplo, acessar um array além dos seus limites), ou de forma deliberada, ao chamar a função panic.

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

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

Rodar este exemplo imprimiria o seguinte no console:

Run panicked with [1, 2, ].

Há 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 seu código. Se a condição em assert! for avaliada como false, o programa vai entrar em panic com uma mensagem de erro do tipo ByteArray. Isso costuma ser usado para verificar suposições durante o desenvolvimento e garantir que os valores atendam a certos critérios.

Por exemplo:

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

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

A notação nopanic

A notação nopanic do Cairo indica que uma função nunca vai entrar em panic.

fn function_never_panic() -> felt252 nopanic {
    108
}

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

O atributo panic_with

O atributo panic_with do Cairo marca uma função que retorna um Option ou um Result e cria uma função wrapper para a sua função anotada, que vai entrar em panic se a função retornar 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 erros de forma elegante, fornecendo feedback em vez de travar ou produzir 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 ? é usado para um tratamento de erros conciso e implícito, deixando a função chamadora lidar com quaisquer erros que possam ocorrer. Quando você usa o operador ? em um tipo Result ou Option, ele faz o seguinte:

  • Se o valor for Result::Ok(x) ou Option::Some(x), ele retorna o valor interno x diretamente.
  • Se o valor for Result::Err(e) ou Option::None, ele propaga o erro ou o None, retornando 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 O link abre em uma nova janela ou aba

Aprenda Tratamento de erros

A prática está bloqueada

Desbloqueie mais 1 exercício para praticar Tratamento de erros