Треки
/
Cairo
Cairo
/
Салабус
/
Обробка помилок
Об

Обробка помилок у Cairo

1 вправа

Про концепцію Обробка помилок

Cairo надає техніки обробки помилок, щоб вирішувати проблеми та керувати помилками, що дозволяє коду виконуватися безперебійно під час запуску.

У Cairo є два типи помилок:

  • Непоправні помилки з panic - ці помилки негайно завершують програму.
  • Поправні помилки з Result - ці помилки можна обробити під час виконання.

Непоправні помилки з panic

У Cairo під час виконання програми можуть виникати непередбачувані проблеми, що призводять до помилок часу виконання, які називають «паніками». Це зупиняє виконання програми, і це корисно, коли продовження не має жодного сенсу. Паніка може виникнути або випадково, через дії, що спричиняють паніку в коді (наприклад, доступ до елемента масиву за його межами), або навмисно, через виклик функції panic.

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

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

Запуск цього прикладу виведе в консолі таке:

Run panicked with [1, 2, ].

Також є спеціальний макрос panic!, який дозволяє використовувати ByteArray як повідомлення про помилку.

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

Макрос assert!

Макрос assert! є корисним інструментом для забезпечення виконання конкретних умов у коді. Якщо умова в assert! набуває значення false, програма панікує з повідомленням про помилку типу ByteArray. Це часто використовують, щоб перевірити припущення під час розробки та переконатися, що значення відповідають певним критеріям.

Наприклад:

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

Якщо x не більше за нуль, програма панікує з повідомленням "x must be greater than zero". assert! допомагає перевіряти інваріанти та передумови без ручного написання коду обробки помилок.

Нотація nopanic

Нотація nopanic у Cairo вказує, що функція ніколи не панікує.

fn function_never_panic() -> felt252 nopanic {
    108
}

Функцію nopanic можна викликати лише у функції, яка також позначена як nopanic.

Атрибут panic_with

Атрибут panic_with у Cairo позначає функцію, яка повертає Option або Result, і створює обгортку для позначеної функції, яка панікує, якщо функція повертає Option::None або 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'
}

Поправні помилки з Result

Перелічення Result дає змогу коректно обробляти помилки, надаючи зворотний звʼязок замість аварійного завершення чи невизначеної поведінки.

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

Розгляньмо приклад того, як ми могли б використати це перелічення для обробки ділення на нуль:

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),
    }
}

Оператор ?

Оператор ? використовують для стислої та неявної обробки помилок, дозволяючи функції, що викликала, мати справу з будь-якими помилками, які можуть виникнути. Коли ми використовуємо оператор ? із типом Result або Option, він робить таке:

  • Якщо значення є Result::Ok(x) або Option::Some(x), він поверне внутрішнє значення x безпосередньо.
  • Якщо значення є Result::Err(e) або Option::None, він передасть помилку або None далі, негайно повернувшись із функції.
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),
    }
}
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Обробка помилок

Практика заблокована

Розблокуйте ще 1 вправу, щоб практикувати концепцію Обробка помилок