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