مد

مدیریت خطا در Cairo

1 تمرین

درباره‌ی مدیریت خطا

Cairo تکنیک‌هایی برای مدیریت خطا ارائه می‌دهد تا بتوانید مشکلات را برطرف کنید و خطاها را مدیریت کنید و کد در حین اجرا روان پیش برود.

در Cairo دو نوع خطا وجود دارد:

  • خطاهای غیرقابل بازیابی با panic: این خطاها برنامه را بلافاصله متوقف می‌کنند.
  • خطاهای قابل بازیابی با Result: این خطاها را می‌توان در زمان اجرا مدیریت کرد.

خطاهای غیرقابل بازیابی با panic

در Cairo ممکن است در طول اجرای برنامه مشکلات پیش‌بینی‌نشده‌ای پیش بیاید که به خطاهای زمان اجرا موسوم به «panic» منجر شود. این وضعیت اجرای برنامه را متوقف می‌کند و همین زمانی به کار می‌آید که ادامه دادن هیچ معنایی نداشته باشد. یک panic ممکن است ناخواسته رخ دهد، از طریق کارهایی که باعث panic در کد می‌شوند (مثلاً دسترسی به آرایه‌ای فراتر از مرزهایش)، یا به‌طور عمدی، با فراخوانی تابع 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 دچار panic می‌شود. از این ماکرو اغلب برای بررسی فرض‌ها در طول توسعه و اطمینان از برآورده شدن معیارهای مشخص توسط مقادیر استفاده می‌شود.

برای نمونه:

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

اگر x بزرگ‌تر از صفر نباشد، برنامه با پیام "x must be greater than zero" دچار panic می‌شود. assert! برای بررسی ناورداها و پیش‌شرط‌ها بدون نوشتن دستی کد مدیریت خطا مفید است.

نشانه‌گذاری nopanic

نشانه‌گذاری nopanic در Cairo نشان می‌دهد که یک تابع هرگز دچار panic نمی‌شود.

fn function_never_panic() -> felt252 nopanic {
    108
}

یک تابع nopanic را فقط می‌توان در تابعی فراخوانی کرد که آن هم با nopanic نشانه‌گذاری شده باشد.

صفت panic_with

صفت panic_with در Cairo تابعی را نشانه‌گذاری می‌کند که Option یا Result برمی‌گرداند و یک تابع پوششی برای تابع نشانه‌گذاری‌شده‌ی شما می‌سازد که اگر تابع Option::None یا Result::Err را برگرداند، دچار panic می‌شود.

#[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 تمرین دیگر را باز کنید